1
0
mirror of https://github.com/taigrr/gopher-os synced 2026-03-25 06:52:27 -07:00

acpi: support compilation of arithmetic and assignment operators to VM opcodes

This commit is contained in:
Achilleas Anagnostopoulos
2017-12-30 18:22:11 +00:00
parent 6f39b2af55
commit b80c337bc7
2 changed files with 380 additions and 2 deletions

View File

@@ -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 {