mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
This commit includes the required code to process all methods defined by an AML entity hierarchy and invoke a function for converting each method statement into a sequence of opcodes that can be executed by our internal VM. The mapping of the AML opcodes to the VM opcodes is facilitated via the opcodeMap which allows us to apply a particular compiler function when converting each AML opcode. Currently, this map is empty so all AML opcodes are mapped to "nop" instructions.
142 lines
5.1 KiB
Go
142 lines
5.1 KiB
Go
package vm
|
|
|
|
import (
|
|
"bytes"
|
|
"gopheros/device/acpi/aml/entity"
|
|
"gopheros/kernel"
|
|
"gopheros/kernel/kfmt"
|
|
)
|
|
|
|
type opMapping struct {
|
|
vmOp uint8
|
|
compilerFn func(*compilerContext, uint8, entity.Entity) *kernel.Error
|
|
}
|
|
|
|
type compilerContext struct {
|
|
rootNS entity.Container
|
|
|
|
// opcodeMap contains the mappings of AML opcodes into a VM opcode plus
|
|
// a compiler function to handle the opcode conversion.
|
|
opcodeMap map[entity.AMLOpcode]opMapping
|
|
|
|
// methodCallMap maps an entity definition to an index in VM.methodCalls.
|
|
methodCallMap map[*entity.Method]uint16
|
|
|
|
vmCtx *Context
|
|
lastErr *kernel.Error
|
|
}
|
|
|
|
func newCompilerContext(rootNS entity.Container) *compilerContext {
|
|
compCtx := &compilerContext{
|
|
rootNS: rootNS,
|
|
methodCallMap: make(map[*entity.Method]uint16),
|
|
vmCtx: new(Context),
|
|
}
|
|
|
|
// the opcodeMap needs to be dynamically populated here instead of
|
|
// 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{}
|
|
|
|
return compCtx
|
|
}
|
|
|
|
// Compile returns a bytecode representation of the AML tree starting at
|
|
// rootNS that can be consumed by the AML VM.
|
|
func Compile(rootNS entity.Container) (*Context, *kernel.Error) {
|
|
compCtx := newCompilerContext(rootNS)
|
|
compileAMLTree(compCtx)
|
|
|
|
if compCtx.lastErr != nil {
|
|
return nil, compCtx.lastErr
|
|
}
|
|
|
|
return compCtx.vmCtx, nil
|
|
}
|
|
|
|
// compileAMLTree converts the AML entity tree inside the supplied
|
|
// compilerContext into a bytecode representation that can be consumed by the
|
|
// host VM. This function is intentionally separate from the above Compile
|
|
// function so tests can easily stub the opcodeMap field of compCtx.
|
|
func compileAMLTree(compCtx *compilerContext) {
|
|
populateMethodMap(compCtx, compCtx.rootNS)
|
|
entity.Visit(0, compCtx.rootNS, entity.TypeMethod, func(_ int, ent entity.Entity) bool {
|
|
if compCtx.lastErr != nil {
|
|
return false
|
|
}
|
|
|
|
compCtx.lastErr = compileMethod(compCtx, ent.(*entity.Method))
|
|
return false
|
|
})
|
|
}
|
|
|
|
// populateMethodMap visits all method entities under root and populates the
|
|
// VM.methodCalls list as well as compCtx.methodCallMap which is used by the
|
|
// asmContext to efficiently encode method calls to the correct index in the
|
|
// methodCalls list.
|
|
func populateMethodMap(compCtx *compilerContext, root entity.Container) {
|
|
entity.Visit(0, root, entity.TypeMethod, func(_ int, ent entity.Entity) bool {
|
|
m := &methodCall{method: ent.(*entity.Method), entrypoint: 0xffffffff}
|
|
compCtx.vmCtx.methodCalls = append(compCtx.vmCtx.methodCalls, m)
|
|
compCtx.methodCallMap[m.method] = uint16(len(compCtx.vmCtx.methodCalls) - 1)
|
|
return false
|
|
})
|
|
}
|
|
|
|
// compileMethod receives an AML method entity and emits the appropriate
|
|
// bytecode for the statements it contains. Calls to compileMethod will also
|
|
// populate the method's entrypoint address which is used by the VM to
|
|
// implement the "call" opcode.
|
|
func compileMethod(compCtx *compilerContext, method *entity.Method) *kernel.Error {
|
|
var errBuf bytes.Buffer
|
|
|
|
// Setup the entrypoint address for this method.
|
|
compCtx.vmCtx.methodCalls[compCtx.methodCallMap[method]].entrypoint = uint32(len(compCtx.vmCtx.bytecode))
|
|
for stmtIndex, stmt := range method.Children() {
|
|
if err := compileStatement(compCtx, stmt); err != nil {
|
|
kfmt.Fprintf(&errBuf, "[%s:%d] %s", method.Name(), stmtIndex, err.Message)
|
|
err.Message = errBuf.String()
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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 {
|
|
mapping, hasMapping := compCtx.opcodeMap[ent.Opcode()]
|
|
if !hasMapping {
|
|
emit8(compCtx, opNop)
|
|
return nil
|
|
}
|
|
|
|
return mapping.compilerFn(compCtx, mapping.vmOp, ent)
|
|
}
|
|
|
|
// emit8 appends an opcode that does not require any operands to the generated
|
|
// bytecode stream.
|
|
func emit8(compCtx *compilerContext, op uint8) {
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, op)
|
|
}
|
|
|
|
// emit16 appends an opcode followed by an word operand to the generated
|
|
// bytecode stream. The word operand will be encoded in big-endian format.
|
|
func emit16(compCtx *compilerContext, op uint8, operand uint16) {
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, op)
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>8)&0xff))
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8(operand&0xff))
|
|
}
|
|
|
|
// emit32 appends an opcode followed by an dword operand to the generated
|
|
// bytecode stream. The word operand will be encoded in big-endian format.
|
|
func emit32(compCtx *compilerContext, op uint8, operand uint32) {
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, op)
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>24)&0xff))
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>16)&0xff))
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>8)&0xff))
|
|
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8(operand&0xff))
|
|
}
|