From b80c337bc7c2ea287b734d3dd75249f7239b292f Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 30 Dec 2017 18:22:11 +0000 Subject: [PATCH] acpi: support compilation of arithmetic and assignment operators to VM opcodes --- src/gopheros/device/acpi/aml/vm/compiler.go | 254 +++++++++++++++++++- src/gopheros/device/acpi/aml/vm/opcodes.go | 128 +++++++++- 2 files changed, 380 insertions(+), 2 deletions(-) diff --git a/src/gopheros/device/acpi/aml/vm/compiler.go b/src/gopheros/device/acpi/aml/vm/compiler.go index 5bb84f0..c3234f1 100644 --- a/src/gopheros/device/acpi/aml/vm/compiler.go +++ b/src/gopheros/device/acpi/aml/vm/compiler.go @@ -7,6 +7,13 @@ import ( "gopheros/kernel/kfmt" ) +type opDirection uint8 + +const ( + opDirectionIn opDirection = iota + opDirectionOut +) + type opMapping struct { vmOp uint8 compilerFn func(*compilerContext, uint8, entity.Entity) *kernel.Error @@ -37,7 +44,28 @@ func newCompilerContext(rootNS entity.Container) *compilerContext { // having a shared, globally initialized map to prevent the Go compiler // from complaining about potential initialization loops (e.g. // compileFn -> compileStatement -> compileFn...) - compCtx.opcodeMap = map[entity.AMLOpcode]opMapping{} + compCtx.opcodeMap = map[entity.AMLOpcode]opMapping{ + entity.OpReturn: {opRet, compileReturn}, + entity.OpStore: {opNop, compileAssignment}, + // Arithmetic opcodes + entity.OpAdd: {opAdd, compileBinaryOperator}, + entity.OpSubtract: {opSub, compileBinaryOperator}, + entity.OpMultiply: {opMul, compileBinaryOperator}, + entity.OpIncrement: {opAdd, compilePostfixOperator}, + entity.OpDecrement: {opSub, compilePostfixOperator}, + entity.OpDivide: {opDiv, compileDivisionOperator}, + entity.OpMod: {opMod, compileBinaryOperator}, + entity.OpShiftLeft: {opShl, compileBinaryOperator}, + entity.OpShiftRight: {opShr, compileBinaryOperator}, + entity.OpAnd: {opAnd, compileBinaryOperator}, + entity.OpOr: {opOr, compileBinaryOperator}, + entity.OpNand: {opNand, compileBinaryOperator}, + entity.OpNor: {opNor, compileBinaryOperator}, + entity.OpXor: {opXor, compileBinaryOperator}, + entity.OpNot: {opNot, compileUnaryOperator}, + entity.OpFindSetLeftBit: {opFindSlb, compileUnaryOperator}, + entity.OpFindSetRightBit: {opFindSrb, compileUnaryOperator}, + } return compCtx } @@ -104,6 +132,230 @@ func compileMethod(compCtx *compilerContext, method *entity.Method) *kernel.Erro return nil } +// compileReturn generates the appropriate opcode stream to returning from a +// method invocation. If the supplied entity contains no operands then this +// function will emit a "ret_void" opcode. +func compileReturn(compCtx *compilerContext, vmOp uint8, ent entity.Entity) *kernel.Error { + operands := ent.Args() + if len(operands) == 0 { + vmOp = opRetVoid + } else if err := compileOperand(compCtx, operands[0], opDirectionIn); err != nil { + return err + } + + emit8(compCtx, vmOp) + return nil +} + +// compileAssignment generates the appropriate opcode stream for handling +// an assignment of one AML entity to another. +func compileAssignment(compCtx *compilerContext, _ uint8, ent entity.Entity) *kernel.Error { + operands := ent.Args() + if len(operands) != 2 { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "unexpected operand count for assignment", + } + } + + // Compile operands + if err := compileOperand(compCtx, operands[0], opDirectionIn); err != nil { + return err + } + return compileOperand(compCtx, operands[1], opDirectionOut) +} + +// compilePostfixOperator generates the appropriate opcode stream for a postfix +// operator (e.g. x++, or x--) that also stores a copy of the result back to +// itself. +func compilePostfixOperator(compCtx *compilerContext, vmOp uint8, ent entity.Entity) *kernel.Error { + operands := ent.Args() + if len(operands) < 1 { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "unexpected operand count for postfix operator " + ent.Opcode().String(), + } + } + + // Compile operands (source, one) + if err := compileOperand(compCtx, operands[0], opDirectionIn); err != nil { + return err + } + emit8(compCtx, opPushOne) + + // Emit opcode for the operator + emit8(compCtx, vmOp) + + // Always store result back to source + return compileOperand(compCtx, operands[0], opDirectionOut) +} + +// compileUnaryOperator generates the appropriate opcode stream for a unary +// operator that optionally stores the results into a second operand. +func compileUnaryOperator(compCtx *compilerContext, vmOp uint8, ent entity.Entity) *kernel.Error { + operands := ent.Args() + if len(operands) < 1 { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "unexpected operand count for unary operator " + ent.Opcode().String(), + } + } + + // Compile operand + if err := compileOperand(compCtx, operands[0], opDirectionIn); err != nil { + return err + } + + // Emit opcode for the operator + emit8(compCtx, vmOp) + + // Store the result if a destination operand is specified + if len(operands) == 2 && !isNilTarget(operands[1]) { + return compileOperand(compCtx, operands[1], opDirectionOut) + } + + return nil +} + +// compileBinaryOperator generates the appropriate opcode stream for a binary +// operator that optionally stores the results into a third operand. +func compileBinaryOperator(compCtx *compilerContext, vmOp uint8, ent entity.Entity) *kernel.Error { + operands := ent.Args() + if len(operands) < 2 { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "unexpected operand count for binary operator " + ent.Opcode().String(), + } + } + + // Compile operands + for opIndex := 0; opIndex < 2; opIndex++ { + if err := compileOperand(compCtx, operands[opIndex], opDirectionIn); err != nil { + return err + } + } + + // Emit opcode for the operator + emit8(compCtx, vmOp) + + // Store the result if a destination operand is specified + if len(operands) == 3 && !isNilTarget(operands[2]) { + return compileOperand(compCtx, operands[2], opDirectionOut) + } + + return nil +} + +// compileDivisionOperator generates the appropriate opcode stream for a +// division operator that optionally stores the remainder and the quotient to +// the optional third and fourth operands. +func compileDivisionOperator(compCtx *compilerContext, vmOp uint8, ent entity.Entity) *kernel.Error { + operands := ent.Args() + if len(operands) < 2 { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "unexpected operand count for operator " + ent.Opcode().String(), + } + } + + // Compile operands + for opIndex := 0; opIndex < 2; opIndex++ { + if err := compileOperand(compCtx, operands[opIndex], opDirectionIn); err != nil { + return err + } + } + + // Emit opcode for the operator + emit8(compCtx, vmOp) + + // After the division, the top of the stack contains the remainder. If + // we need to store it emit a store opcode before popping it of the + // stack + if len(operands) >= 3 && !isNilTarget(operands[2]) { + if err := compileOperand(compCtx, operands[2], opDirectionOut); err != nil { + return err + } + } + emit8(compCtx, opPop) + + // The top of the stack now contains the quotient which can optionally be + // stored in the fourth operand + if len(operands) == 4 && !isNilTarget(operands[3]) { + return compileOperand(compCtx, operands[3], opDirectionOut) + } + return nil +} + +// compileOperand generates the appropriate opcode stream for an operator's +// opcode. The dir argument controls whether the operand is read from or +// written to. As AML statements may contain nested statements as operands, +// this function will fallback to a call to compileStatement if the operand is +// not a local arg, method arg or a constant. +func compileOperand(compCtx *compilerContext, arg interface{}, dir opDirection) *kernel.Error { + ent, isEnt := arg.(entity.Entity) + if !isEnt { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "compileArg: arg must be an AML entity", + } + } + entOp := ent.Opcode() + + if entity.OpIsLocalArg(entOp) { + outOp := opPushLocal0 + if dir == opDirectionOut { + outOp = opStoreLocal0 + } + + emit8(compCtx, outOp+uint8(entOp-entity.OpLocal0)) + return nil + } else if entity.OpIsMethodArg(entOp) { + outOp := opPushArg0 + if dir == opDirectionOut { + outOp = opStoreArg0 + } + + emit8(compCtx, outOp+uint8(entOp-entity.OpArg0)) + return nil + } else if constant, isConst := ent.(*entity.Const); isConst { + if dir == opDirectionOut { + return &kernel.Error{ + Module: "acpi_aml_compiler", + Message: "compileArg: attempt to store value to a constant", + } + } + + // Special constants + switch constant.Value { + case uint64(0): + emit8(compCtx, opPushZero) + case uint64(1): + emit8(compCtx, opPushOne) + case uint64((1 << 64) - 1): + emit8(compCtx, opPushOnes) + default: + compCtx.vmCtx.constants = append(compCtx.vmCtx.constants, constant) + emit16(compCtx, opPushConst, uint16(len(compCtx.vmCtx.constants)-1)) + } + return nil + } + + return compileStatement(compCtx, ent) +} + +// isNilTarget returns true if t is nil or a nil const entity. +func isNilTarget(target interface{}) bool { + if target == nil { + return true + } + + if ent, ok := target.(*entity.Const); ok { + return ent.Value != nil && ent.Value != uint64(0) + } + + return false +} + // compileStatement receives as input an AML entity that represents a method // statement and emits the appropriate bytecode for executing it. func compileStatement(compCtx *compilerContext, ent entity.Entity) *kernel.Error { diff --git a/src/gopheros/device/acpi/aml/vm/opcodes.go b/src/gopheros/device/acpi/aml/vm/opcodes.go index ce93324..168194b 100644 --- a/src/gopheros/device/acpi/aml/vm/opcodes.go +++ b/src/gopheros/device/acpi/aml/vm/opcodes.go @@ -13,8 +13,134 @@ package vm // | mnemonic | opcode | operands [count]: [operand labels] | stack before | stack after | description | // |------------|--------|-------------------------------------------------------|------------------|----------------------------------|--------------------------------------------------------------------------------------------------------------| // | nop | 0x00 | | | | nop | +// | push_0 | 0x01 | | | 0 | push uint64(0) | +// | push_1 | 0x02 | | | 1 | push uint64(1) | +// | push_ones | 0x03 | | | MaxUint64 | push uint64(MaxUint64) | +// | push_l0 | 0x04 | | | &Local0 | push local0 address | +// | push_l1 | 0x05 | | | &Local1 | push local1 address | +// | push_l2 | 0x06 | | | &Local2 | push local2 address | +// | push_l3 | 0x07 | | | &Local3 | push local3 address | +// | push_l4 | 0x08 | | | &Local4 | push local4 address | +// | push_l5 | 0x09 | | | &Local5 | push local5 address | +// | push_l6 | 0x0a | | | &Local6 | push local6 address | +// | push_l7 | 0x0b | | | &Local7 | push local7 address | +// | push_a0 | 0x08 | | | &Arg0 | push arg0 address | +// | push_a1 | 0x0d | | | &Arg1 | push arg1 address | +// | push_a2 | 0x0e | | | &Arg2 | push arg2 address | +// | push_a3 | 0x0f | | | &Arg3 | push arg3 address | +// | push_a4 | 0x10 | | | &Arg4 | push arg4 address | +// | push_a5 | 0x11 | | | &Arg5 | push arg5 address | +// | push_a6 | 0x12 | | | &Arg6 | push arg6 address | +// | push_buf | 0x40 | 2: indexbyte1, indexbyte2 | | entity ptr | push entity address for buffer pool index (indexbyte1<<8 + indexbyte2) | +// | push_const | 0x41 | 2: indexbyte1, indexbyte2 | | entity ptr | push entity address for const pool index (indexbyte1<<8 + indexbyte2) | +// | push_pkg | 0x42 | 2: indexbyte1, indexbyte2 | | entity ptr | push entity address for package pool index (indexbyte1<<8 + indexbyte2) | +// | | | | | | | +// | store_l0 | 0x13 | | value | | store value at local0 with no conversion | +// | store_l1 | 0x14 | | value | | store value at local1 with no conversion | +// | store_l2 | 0x15 | | value | | store value at local2 with no conversion | +// | store_l3 | 0x16 | | value | | store value at local3 with no conversion | +// | store_l4 | 0x17 | | value | | store value at local4 with no conversion | +// | store_l5 | 0x18 | | value | | store value at local5 with no conversion | +// | store_l6 | 0x19 | | value | | store value at local6 with no conversion | +// | store_l7 | 0x1a | | value | | store value at local7 with no conversion | +// | store_a0 | 0x1b | | value | | store value at arg0 with no conversion | +// | store_a1 | 0x1c | | value | | store value at arg1 with no conversion | +// | store_a2 | 0x1d | | value | | store value at arg2 with no conversion | +// | store_a3 | 0x1e | | value | | store value at arg3 with no conversion | +// | store_a4 | 0x1f | | value | | store value at arg4 with no conversion | +// | store_a5 | 0x20 | | value | | store value at arg5 with no conversion | +// | store_a6 | 0x21 | | value | | store value at arg6 with no conversion | +// | store | 0x22 | | dst, value | | store value to dst after applying implicit type conversion | +// | | | | | | | +// | pop | 0x23 | | value | | pop and discard top value of the stack | +// | | | | | | | +// | jmp | 0x80 | 4: offsetbyte1, offsetbyte2, offsetbyte3, offsetbyte4 | | | jump to abs address (offsetbyte1<<24 + offsetbyte2<<16 + offsetbyte3<<8 + offsetbyte4) | +// | je | 0x81 | 4: offsetbyte1, offsetbyte2, offsetbyte3, offsetbyte4 | value1, value2 | | jump to abs address (offsetbyte1<<24 + offsetbyte2<<16 + offsetbyte3<<8 + offsetbyte4) if value1 == value2 | +// | jl | 0x82 | 4: offsetbyte1, offsetbyte2, offsetbyte3, offsetbyte4 | value1, value2 | | jump to abs address (offsetbyte1<<24 + offsetbyte2<<16 + offsetbyte3<<8 + offsetbyte4) if value1 < value2 | +// | jg | 0x83 | 4: offsetbyte1, offsetbyte2, offsetbyte3, offsetbyte4 | value1, value2 | | jump to abs address (offsetbyte1<<24 + offsetbyte2<<16 + offsetbyte3<<8 + offsetbyte4) if value1 > value2 | +// | | | | | | | +// | call | 0x43 | 2: indexbyte1, indexbyte2 | value1,...valueN | result | call method at method pool index (indexbyte1<<8 + indexbyte2) and replace args on stack with the ret value | +// | ret_void | 0x24 | | | | return from method, unwind stack and replace arg list | +// | ret | 0x25 | | value | value | return from method, unwind stack and replace arg list with return value | +// | | | | | | | +// | add | 0x26 | | value1, value2 | value1 + value2 | perform integer addition | +// | sub | 0x27 | | value1, value2 | value1 - value2 | perform integer subtraction | +// | mul | 0x28 | | value1, value2 | value1 * value2 | perform integer multiplication | +// | div | 0x29 | | value1, value2 | value1 / value2, value1 % value2 | perform integer division | +// | mod | 0x2a | | value1, value2 | value1 % value2 | perform integer module calculation | +// | | | | | | | +// | shl | 0x2b | | value1, value2 | value1 << value2 | shift value1 left | +// | shr | 0x2c | | value1, value2 | value1 >> value2 | shift value1 right | +// | and | 0x2d | | value1, value2 | value1 & value2 | bitwise and | +// | or | 0x2e | | value1, value2 | value1 | value2 | bitwise or | +// | nand | 0x2f | | value1, value2 | value1 &^ value2 | bitwise nand | +// | nor | 0x30 | | value1, value2 | ^(value1 | value2) | bitwise nor | +// | xor | 0x31 | | value1, value2 | value1 ^ value2 | bitwise xor | +// | not | 0x32 | | value | !value % value2 | bitwise not | +// | fslb | 0x33 | | value | index of left-most set bit or 0 | find 1-based index of left-most set bit | +// | fsrb | 0x34 | | value | index of right-most set bit or 0 | find 1-based index of right-most set bit | const ( - opNop uint8 = 0x00 + opNop uint8 = 0x00 + opPushZero uint8 = 0x01 + opPushOne uint8 = 0x02 + opPushOnes uint8 = 0x03 + opPushLocal0 uint8 = 0x04 + opPushLocal1 uint8 = 0x05 + opPushLocal2 uint8 = 0x06 + opPushLocal3 uint8 = 0x07 + opPushLocal4 uint8 = 0x08 + opPushLocal5 uint8 = 0x09 + opPushLocal6 uint8 = 0x0a + opPushLocal7 uint8 = 0x0b + opPushArg0 uint8 = 0x0c + opPushArg1 uint8 = 0x0d + opPushArg2 uint8 = 0x0e + opPushArg3 uint8 = 0x0f + opPushArg4 uint8 = 0x10 + opPushArg5 uint8 = 0x11 + opPushArg6 uint8 = 0x12 + opPushBuffer uint8 = 0x40 + opPushConst uint8 = 0x41 + opPushPkg uint8 = 0x42 + opStoreLocal0 uint8 = 0x13 + opStoreLocal1 uint8 = 0x14 + opStoreLocal2 uint8 = 0x15 + opStoreLocal3 uint8 = 0x16 + opStoreLocal4 uint8 = 0x17 + opStoreLocal5 uint8 = 0x18 + opStoreLocal6 uint8 = 0x19 + opStoreLocal7 uint8 = 0x1a + opStoreArg0 uint8 = 0x1b + opStoreArg1 uint8 = 0x1c + opStoreArg2 uint8 = 0x1d + opStoreArg3 uint8 = 0x1e + opStoreArg4 uint8 = 0x1f + opStoreArg5 uint8 = 0x20 + opStoreArg6 uint8 = 0x21 + opStore uint8 = 0x22 + opPop uint8 = 0x23 + opJmp uint8 = 0x80 + opJe uint8 = 0x81 + opJl uint8 = 0x82 + opJg uint8 = 0x83 + opCall uint8 = 0x43 + opRetVoid uint8 = 0x24 + opRet uint8 = 0x25 + opAdd uint8 = 0x26 + opSub uint8 = 0x27 + opMul uint8 = 0x28 + opDiv uint8 = 0x29 + opMod uint8 = 0x2a + opShl uint8 = 0x2b + opShr uint8 = 0x2c + opAnd uint8 = 0x2d + opOr uint8 = 0x2e + opNand uint8 = 0x2f + opNor uint8 = 0x30 + opXor uint8 = 0x31 + opNot uint8 = 0x32 + opFindSlb uint8 = 0x33 // find set left bit + opFindSrb uint8 = 0x34 // find set right bit ) // instrEncodingLen returns the number of bytes that encode the instruction