HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component util.compiler.CodeGen by barry
expand copy to clipboardexpand
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)
		}
	
	}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n util.compiler.CodeGen -m "reason for update" -u yourUsername
Version 2 by barry
Version 1 (this version) by barry
Notes for this version: New compiler components