uses HWI
uses HWID
data RelocHolder {
int relocations[]
}
data OpMap {
OpToken op
HWInstruction hwi
}
data ConInt64 {
int8 value
}
data ConInt32 {
int4 value
}
data ExitSequence {
HWInstruction setExitAddressOps[]
HWInstruction exitStream[]
}
data GenState {
int lastLineNumber
}
data RefPatch {
OpToken patchFrom
OpToken patchTo
}
data HWStreamData {
HWInstruction stream[]
OpMap map[]
int registerCount
byte registerPurge[]
byte registerPurgeD[]
}
const byte TYPE_NULL = 0
const byte TYPE_LITERAL = 1
const byte TYPE_PATTERN = 2
const byte TYPE_FUNCTION = 3
const byte TYPE_UNUSED_D = 4
const byte TYPE_OBJECT = 5
const byte TYPE_DATA = 6
const byte TYPE_ARRAY = 7
const byte TYPE_VAR = 8
const byte TYPE_UNUSED_C = 9
const byte TYPE_EVENTSOURCE = 10
const byte TYPE_UNUSED_A = 11
const byte TYPE_UNUSED_B = 12
const byte TYPE_PTRH = 13
const byte TYPE_DECIMAL = 14
const int OP_ID_NT_IFXNR = 256
const int OP_ID_NT_LOCALS_GET_PTR = 300
const int OP_ID_NT_LOCALS_GET_REF = 301
const int OP_ID_NT_LOCALS_STORE_GET_REF = 302
const bool DEBUG_INS_STREAM = false
interface CodeGenLib {
HWStreamData makeHWIStream(OpToken instructions[], DanaType types[], LiteralEntry literals[], TypeOffsetEntry typeTable[], TypeMapping typeMap[], int hwidSet[], int addressWidth, byte endianness)
void setHWIOffset(HWInstruction ins, int offset)
void setHWIRefAddress(HWInstruction ins, int address)
}
component provides CodeGen requires io.Output out, data.IntUtil iu, HWI_Info hwiInfo, data.adt.List, data.adt.Stack, data.query.Search search, native CodeGenLib lib {
bool useQIK = true
CodeGen:CodeGen()
{
}
void setConValue(ConInt32 v32, ConInt64 v64, int8 value, int addressWidth)
{
if (addressWidth == 8)
v64.value = value
else if (addressWidth == 4)
v32.value = value
}
byte[] endianSwap(byte bytes[])
{
byte result[] = new byte[bytes.arrayLength]
int rindex = result.arrayLength - 1
for (int i = 0; i < result.arrayLength; i++)
{
result[rindex - i] = bytes[i]
}
return result
}
bool opcodeIncrementsRegister(int iid)
{
if (iid == OP_ID_NT_IFXNR)
return false
else
return hwiInfo.instructionIncrementsRegister(iid)
}
void nni_writeVMInstruction(File fd, Code codeHW, FunctionInfo function, int native_frame_size, int nniTableOffset, HWInstruction i, int textOffset, GenState gs, RelocHolder rh)
{
if (i.iid == HWI.OP_ID_JMP)
{
//construct a direct jump instruction
//insert a thread kill detection instruction, if we're jumping to earlier in the instruction stream (i.e., a loop)
if (useQIK)
{
if (i.refBefore)
{
HWInstruction ins = new HWInstruction(HWI.OP_ID_FRAME_QIK)
codeHW.writeVMInstruction(fd, function, native_frame_size, nniTableOffset, ins, textOffset, useQIK)
}
}
lib.setHWIRefAddress(i, codeHW.writeJump(fd, i, textOffset))
return
}
else
{
// -- parameters to instruction --
int instructionStartPos = (fd.getPos() - textOffset)
int ins_code = i.iid
//write a new line number, if needed
if (i.lineNumber != gs.lastLineNumber && i.lineNumber != 0)
{
HWInstruction ins = new HWInstruction(HWI.OP_ID_SET_LINE_NUMBER)
ins.ex1 = i.lineNumber
codeHW.writeVMInstruction(fd, function, native_frame_size, nniTableOffset, ins, textOffset, useQIK)
gs.lastLineNumber = i.lineNumber
}
//write the instruction
VMIResult vmir = codeHW.writeVMInstruction(fd, function, native_frame_size, nniTableOffset, i, textOffset, useQIK)
lib.setHWIRefAddress(i, vmir.refAddress)
rh.relocations = new int[](rh.relocations, vmir.relocations)
// -- conditional jump ? --
if (ins_code == HWI.OP_ID_IF
|| ins_code == HWI.OP_ID_IFX
|| ins_code == HWI.OP_ID_IFXN)
{
//we've just executed an "if", so here we insert a conditional jump instruction based on the if's return value
// - we jump to the location indicated by the instruction...
// - for this we insert a testb and then a jne
lib.setHWIRefAddress(i, codeHW.writeConditionalJump(fd, i, textOffset))
}
//re-entrant instruction?
if (ins_code == HWI.OP_ID_STACK_FRAME_INIT_OBJECT
|| ins_code == HWI.OP_ID_DYNAMIC_CALL
|| ins_code == HWI.OP_ID_NEW_OBJECT
|| ins_code == HWI.OP_ID_NEW_OBJECT_ST
|| ins_code == HWI.OP_ID_NEW_OBJECT_CONSTRUCT
|| ins_code == HWI.OP_ID_NEW_OBJECT_CONSTRUCT_ST)
{
codeHW.writeConditionalRepeat(fd, textOffset, instructionStartPos)
}
}
}
OpToken getFirstExecutedOp(OpToken op)
{
while (op.parameters != null)
{
op = op.parameters[0]
}
return op
}
HWInstruction getOpStart(HWInstruction ins, OpMap opMap[])
{
//find the op
OpToken match = null
for (int i = 0; i < opMap.arrayLength; i++)
{
OpMap ot = opMap[i]
if (ot.hwi === ins)
{
//find its first parameter
match = getFirstExecutedOp(ot.op)
break
}
}
if (match == null) out.println("[CG-error] op start not found")
//get its corresponding instruction
for (int i = 0; i < opMap.arrayLength; i++)
{
OpMap ot = opMap[i]
if (ot.op === match)
{
return ot.hwi
}
}
out.println("[CG-error] hwi of op start not found, line $(ins.lineNumber), op $(ins.iid)")
return null
}
void writeMutexUnlockSequence(File fd, Code codeHW, FunctionInfo function, HWInstruction hwis[], int at, OpMap opMap[], int nativeFrameSize, int nniTableOffset, int textOffset, GenState gs, RelocHolder reloc)
{
for (int j = at + 1; j < hwis.arrayLength; j++)
{
if (hwis[j] === hwis[at].ref)
{
break
}
else if (hwis[j].iid == HWI.OP_ID_MUTEX_UNLOCK)
{
//duplicate this instruction & its parameters
HWInstruction hwc = getOpStart(hwis[j], opMap)
if (hwc == null) out.println("[CG-error] hwi of op start not found, line $(hwis[at].lineNumber), op $(hwis[at].iid)")
int oj = j
while (hwis[j] !== hwc)
{
j --
}
while (j != oj)
{
if (hwis[j].iid != HWI.OP_ID_GET_PR)
{
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, hwis[j], textOffset, gs, reloc)
}
j ++
}
if (hwis[j].iid != HWI.OP_ID_GET_PR)
{
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, hwis[j], textOffset, gs, reloc)
}
}
}
}
HWInstruction[] generateMutexUnlockSequence(HWInstruction hwis[], int at, OpMap opMap[], int nativeFrameSize, int nniTableOffset, int textOffset, RelocHolder reloc)
{
HWInstruction result[]
for (int j = at + 1; j < hwis.arrayLength; j++)
{
if (hwis[j] === hwis[at].ref)
{
break
}
else if (hwis[j].iid == HWI.OP_ID_MUTEX_UNLOCK)
{
//duplicate this instruction & its parameters
HWInstruction hwc = getOpStart(hwis[j], opMap)
if (hwc == null) out.println("[CG-error] hwi of op start not found, line $(hwis[at].lineNumber), op $(hwis[at].iid)")
int oj = j
while (hwis[j] !== hwc)
{
j --
}
while (j != oj)
{
result = new HWInstruction[](result, hwis[j])
j ++
}
result = new HWInstruction[](result, hwis[j])
}
}
return result
}
//NOTE: "instructions" here does not need to be "store"; it's passed into a List.add() operation, but that List is itself a local variable :-S
// - currently the compiler/runtime can't determine that this is a non-store scenario...
CGResult CodeGen:generate(File fd, Code codeHW, store OpToken instructions[], DanaType types[], LiteralsTable literals, TypeTable typeTable, TypeMapping typeMap[], FunctionInfo function, int textOffset, int nniTableOffset, int hwidSet[])
{
int addressWidth = codeHW.getAddressWidth()
ConInt64 con64 = new ConInt64()
ConInt32 con32 = new ConInt32()
byte conBytes[] = null
if (addressWidth == 8)
conBytes = dana.serial(con64)
else if (addressWidth == 4)
conBytes = dana.serial(con32)
int hdrOffset = function.textOffset
int exPos = function.exPos
int codeStart = function.codeStart
int csA = fd.getPos()
HWInstruction ins = new HWInstruction()
int registerCount
List exitSequences = new List()
Stack exitSeqStack = new Stack()
RelocHolder reloc = new RelocHolder()
GenState gstate = new GenState()
CallRefAddress callRefs[]
HWStreamData hwsd = lib.makeHWIStream(instructions, types, literals.getOffsets(), typeTable.getOffsets(), typeMap, hwidSet, addressWidth, codeHW.getEndianness())
HWInstruction hwis[] = clone hwsd.stream
registerCount = hwsd.registerCount
WFEResult wfeRes = codeHW.writeFunctionEntry(fd, hwis, registerCount, textOffset, nniTableOffset)
reloc.relocations = new int[](reloc.relocations, wfeRes.relocations)
int nativeFrameSize = wfeRes.nativeFrameSize
if (DEBUG_INS_STREAM) out.println("-- writing function instructions, total register count $registerCount --")
//stack frame details, for exceptions (this tells the VM where the return address of this function will be on the stack):
ins = new HWInstruction(HWI.OP_ID_SET_RETURN_ADDRESS)
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, ins, textOffset, gstate, reloc)
if (DEBUG_INS_STREAM) out.println(" > $(ins.iid)")
for (int i = 0; i < registerCount; i++)
{
ins = new HWInstruction(HWI.OP_ID_CLEAR_REGISTER)
if (hwsd.registerPurge[i] || hwsd.registerPurgeD[i])
{
ins.registers = new int[](i)
codeHW.writeVMInstruction(fd, function, nativeFrameSize, nniTableOffset, ins, textOffset, useQIK)
if (DEBUG_INS_STREAM) out.println(" > $(ins.iid) for r$(i)")
}
}
for (int i = 0; i < hwis.arrayLength; i++)
{
hwis[i] = hwis[i]
if (hwis[i].iid != HWI.OP_ID_GET_PR)
{
if (DEBUG_INS_STREAM)
{
out.print(" > $(hwis[i].iid)")
for (int j = 0; j < hwis[i].registers.arrayLength; j++)
{
out.print(" r$(hwis[i].registers[j])")
}
out.print(" | $(hwis[i].ex1) $(hwis[i].ex2) $(hwis[i].ex3) $(hwis[i].ex4)")
out.println("")
}
//for any jump instructions which jump forwards, this might be a break statement, which may exit a mutex block
// - in this case we need to generate mutex unlock instructions just before the break statement
if (hwis[i].iid == HWI.OP_ID_JMP && hwis[i].ref.offset == 0)
{
writeMutexUnlockSequence(fd, codeHW, function, hwis, i, hwsd.map, nativeFrameSize, nniTableOffset, textOffset, gstate, reloc)
}
//hwis[i].offset = fd.getPos() - textOffset
lib.setHWIOffset(hwis[i], fd.getPos() - textOffset)
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, hwis[i], textOffset, gstate, reloc)
if (hwis[i].iid == HWI.OP_ID_MUTEX_LOCK)
{
//generate a new exit sequence, in case of return operators or runtime exceptions
ins = new HWInstruction(HWI.OP_ID_SET_EXIT_ADDRESS)
ins.offset = fd.getPos() - textOffset
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, ins, textOffset, gstate, reloc)
HWInstruction exitStream[] = generateMutexUnlockSequence(hwis, i, hwsd.map, nativeFrameSize, nniTableOffset, textOffset, reloc)
ExitSequence nes = new ExitSequence(ins, exitStream)
exitSequences.add(nes)
exitSeqStack.push(nes)
}
else if (hwis[i].iid == HWI.OP_ID_MUTEX_UNLOCK)
{
//update the exit sequence address appropriately
exitSeqStack.pop()
ExitSequence es = exitSeqStack.peek()
if (es != null)
{
ins = new HWInstruction(HWI.OP_ID_SET_EXIT_ADDRESS)
ins.offset = fd.getPos() - textOffset
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, ins, textOffset, gstate, reloc)
es.setExitAddressOps = new HWInstruction[](es.setExitAddressOps, ins)
}
else
{
ins = new HWInstruction(HWI.OP_ID_SET_RETURN_ADDRESS)
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, ins, textOffset, gstate, reloc)
}
}
}
else
{
//hwis[i].offset = fd.getPos() - textOffset
lib.setHWIOffset(hwis[i], fd.getPos() - textOffset)
codeHW.writeNOP(fd)
}
}
//now perform a second pass over hwis, and patch all cross-references
for (int i = 0; i < hwis.arrayLength; i++)
{
if (hwis[i].ref != null)
{
if (hwis[i].iid == HWI.OP_ID_IF || hwis[i].iid == HWI.OP_ID_IFX || hwis[i].iid == HWI.OP_ID_IFXN)
{
codeHW.patchConditionalJump(fd, hwis[i], textOffset)
}
else if (hwis[i].iid == HWI.OP_ID_JMP)
{
codeHW.patchJump(fd, hwis[i], textOffset)
}
}
else if (hwis[i].iid == HWI.OP_ID_STACK_FRAME_INIT_LOCAL)
{
callRefs = new CallRefAddress[](callRefs, new CallRefAddress(null, hwis[i].functionNameRef, hwis[i].refAddress))
//out.println("add callref for $(hwis[i].functionNameRef) :: $(hwis[i].refAddress)")
}
}
// -- alternative exit sequences, for mutex release --
ExitSequence esq = exitSequences.getFirst()
if (esq != null)
{
//out.println("--- ESQ's --")
//write a jump instruction, which jumps over all of the exit sequences
ins = new HWInstruction(HWI.OP_ID_JMP)
HWInstruction hjIns[] = ins
ins.refAddress = codeHW.writeJump(fd, ins, textOffset)
for (; esq != null; esq = exitSequences.getNext())
{
//out.println("write ESQ for $(fd.getPos())")
//update the reference address of (all of) the OP_ID_SET_EXIT_ADDRESS instructions associated with this esq
int ejl = fd.getPos() - csA
for (int i = 0; i < esq.setExitAddressOps.arrayLength; i++)
{
//out.println(" -- PSEA $(esq.setExitAddressOps[i].offset) :: $(ejl)")
codeHW.patchSetExitAddress(fd, esq.setExitAddressOps[i], ejl, textOffset)
}
//write the instructions, plus a jump instruction to jump to the end of all the exit sequences
//out.println(" --> writing exit sequence")
for (int i = 0; i < esq.exitStream.arrayLength; i++)
{
if (esq.exitStream[i].iid != HWI.OP_ID_GET_PR)
{
nni_writeVMInstruction(fd, codeHW, function, nativeFrameSize, nniTableOffset, esq.exitStream[i], textOffset, gstate, reloc)
}
//out.println(" :: $(esq.exitStream[i].iid)")
}
ins = new HWInstruction(HWI.OP_ID_JMP)
hjIns = new HWInstruction[](hjIns, ins)
ins.refAddress = codeHW.writeJump(fd, ins, textOffset)
}
ins = new HWInstruction(HWI.OP_ID_NOP)
ins.offset = fd.getPos() - textOffset
codeHW.writeNOP(fd)
for (int i = 0; i < hjIns.arrayLength; i++)
{
hjIns[i].ref = ins
codeHW.patchJump(fd, hjIns[i], textOffset)
}
}
int csB = fd.getPos()
int JP = hdrOffset + codeStart + (csB - csA)
// -- standard exit sequence --
codeHW.writeFunctionPreExit(fd, nativeFrameSize)
// -- register cleanup --
for (int q = 0; q < registerCount; q++)
{
ins = new HWInstruction(HWI.OP_ID_PURGE_REGISTER)
//printf("PURGE %u\n", rIndex);
if (hwsd.registerPurge[q])
{
ins.registers = new int[](q)
codeHW.writeVMInstruction(fd, function, nativeFrameSize, nniTableOffset, ins, textOffset, useQIK)
}
else if (hwsd.registerPurgeD[q])
{
ins.iid = HWI.OP_ID_PURGE_REGISTER_DYNAMIC
ins.registers = new int[](q)
codeHW.writeVMInstruction(fd, function, nativeFrameSize, nniTableOffset, ins, textOffset, useQIK)
}
}
codeHW.writeFunctionExit(fd, nativeFrameSize)
// -- go back and update the function-exit jump location --
// - note that the envelope writer should already have added a relocation to this offset, so we don't need to add a new relocation here
int op = fd.getPos()
fd.setPos(exPos)
setConValue(con32, con64, JP, addressWidth)
fd.write(endianSwap(conBytes))
fd.setPos(op)
return new CGResult(reloc.relocations, callRefs)
}
void CodeGen:patchCallAddress(File fd, Code codeHW, int refAddress, int callAddress)
{
codeHW.patchCallAddress(fd, new HWInstruction(refAddress = refAddress), callAddress)
}
}