Added a asm gen prototype.
This commit is contained in:
parent
53f4c7758b
commit
deeaccd3d7
3 changed files with 277 additions and 1 deletions
258
compiler/asmgen.nim
Normal file
258
compiler/asmgen.nim
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
import cgendata, ast, strutils, os, extccomp, options, ropes, strutils, astalgo
|
||||
import msgs
|
||||
|
||||
type
|
||||
OpCode = enum
|
||||
opcPush, opcMov, opcPop, opcRet, opcAdd, opcSub, opcLeave, opcCmp, opcJe,
|
||||
opcJmp, opcJne
|
||||
|
||||
Register = enum
|
||||
regRAX, regRDI, regRSI, regRDX, regRCX, regR8, regR9,
|
||||
regRBP, regRSP
|
||||
|
||||
ProcSection = enum
|
||||
prcHeader, prcBody
|
||||
|
||||
AsmProc = ref object
|
||||
stackSlotsCounter: int ## How many stack slots have been used by vars.
|
||||
owner: PSym ## The owning nkProcDef
|
||||
code: array[ProcSection, string]
|
||||
ifStmtNum: int
|
||||
elifStmtNum: int
|
||||
|
||||
const
|
||||
noOperand = {opcRet, opcLeave}
|
||||
oneOperand = {opcPush, opcPop, opcJe, opcJmp, opcJne}
|
||||
twoOperands = {opcMov, opcAdd, opcSub, opcCmp}
|
||||
|
||||
template codeBody(): expr =
|
||||
context.code[prcBody]
|
||||
|
||||
template codeHeader(): expr =
|
||||
context.code[prcHeader]
|
||||
|
||||
proc `$`(opc: OpCode): string =
|
||||
result = system.`$`(opc)[3 .. -1].toUpper
|
||||
|
||||
proc addN(s: var string, y: string) =
|
||||
s.add y & "\n"
|
||||
|
||||
proc addIndentN(s: var string, y: string) =
|
||||
s.addN "\t" & y
|
||||
|
||||
proc emit(s: var string, opc: OpCode) =
|
||||
assert opc in noOperand
|
||||
s.addIndentN($opc)
|
||||
|
||||
proc emit(s: var string, opc: OpCode, x: string) =
|
||||
assert opc in oneOperand
|
||||
s.addIndentN($opc & " " & x)
|
||||
|
||||
proc emit(s: var string, opc: OpCode, x, y: string) =
|
||||
assert opc in twoOperands
|
||||
s.addIndentN($opc & " " & x & ", " & y)
|
||||
|
||||
proc `$`(reg: Register): string =
|
||||
result = system.`$`(reg)[3 .. -1].toUpper
|
||||
|
||||
proc getProcName(context: AsmProc): string =
|
||||
context.owner.loc.r.ropeToStr
|
||||
|
||||
proc getRegisterParam(pos: int): Register =
|
||||
# TODO Windows
|
||||
const paramToRegister = [regRDI, regRSI, regRDX, regRCX, regR8, regR9]
|
||||
if pos > 5: internalError("TODO: Too many proc params")
|
||||
return paramToRegister[pos]
|
||||
|
||||
proc getBlockLabel(context: AsmProc, sym: PSym): string =
|
||||
context.getProcName() & '.' & sym.name.s & '_' & sym.id
|
||||
|
||||
proc toOperand(n: PNode): string =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
case n.sym.kind
|
||||
of skParam:
|
||||
return $getRegisterParam(n.sym.position)
|
||||
of skVar:
|
||||
let name = n.sym.name.s & '_' & $n.sym.id
|
||||
return "[" & $regRSP & "+" & name & "]"
|
||||
else:
|
||||
assert false, $n.sym.kind
|
||||
of nkIntLit:
|
||||
return $n.intVal
|
||||
else:
|
||||
assert false, $n.kind
|
||||
|
||||
proc genAsgn(context: AsmProc, n: PNode, dest: Register)
|
||||
proc genMagicExpr(context: AsmProc, magic: TMagic, args: seq[PNode],
|
||||
dest: Register) =
|
||||
for i in 0 .. <args.len:
|
||||
case args[i].kind
|
||||
of nkSym, nkIntLit:
|
||||
if i == 0:
|
||||
codeBody.emit(opcMov, $dest, toOperand(args[i]))
|
||||
continue
|
||||
case magic
|
||||
of mAddI:
|
||||
codeBody.emit(opcAdd, $dest, toOperand(args[i]))
|
||||
of mSubI:
|
||||
codeBody.emit(opcSub, $dest, toOperand(args[i]))
|
||||
else:
|
||||
assert false, "Unknown magic: " & $magic
|
||||
else:
|
||||
genAsgn(context, args[i], dest)
|
||||
|
||||
proc genMagicCmp(context: AsmProc, magic: TMagic, args: seq[PNode],
|
||||
label: string) =
|
||||
case magic
|
||||
of mEqI:
|
||||
codeBody.emit(opcCmp, toOperand(args[0]), toOperand(args[1]))
|
||||
codeBody.emit(opcJne, label)
|
||||
else:
|
||||
assert false, "Unknown magic: " & $magic
|
||||
|
||||
proc genAsgn(context: AsmProc, n: PNode, dest: Register) =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
assert n.sons[0].kind == nkSym
|
||||
if n.sons[0].sym.magic != mNone:
|
||||
genMagicExpr(context, n.sons[0].sym.magic, n.sons[1 .. -1], dest)
|
||||
else:
|
||||
assert false
|
||||
of nkSym, nkIntLit:
|
||||
codeBody.emit(opcMov, $dest, toOperand(n))
|
||||
else:
|
||||
assert false, $n.kind
|
||||
|
||||
proc genIfCond(context: AsmProc, n: PNode, label: string) =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
assert n.sons[0].kind == nkSym
|
||||
assert n.sons[0].sym.magic != mNone
|
||||
context.genMagicCmp(n.sons[0].sym.magic, n.sons[1 .. -1], label)
|
||||
else:
|
||||
assert false, $n.kind
|
||||
|
||||
proc genVars(context: AsmProc, n: PNode) =
|
||||
assert n.kind == nkIdentDefs
|
||||
var i = 0
|
||||
while i < n.sons.len:
|
||||
assert n.sons[i].kind == nkSym
|
||||
debug n.sons[i].sym
|
||||
let name = n.sons[i].sym.name.s & '_' & $n.sons[i].sym.id
|
||||
let offset = context.stackSlotsCounter * 8
|
||||
codeBody.addIndentN(name & " equ " & $offset)
|
||||
codeBody.emit(opcMov, "QWORD " & toOperand(n.sons[i]), toOperand(n.sons[i+2]))
|
||||
context.stackSlotsCounter.inc
|
||||
i.inc 3
|
||||
|
||||
proc genProcBody(context: AsmProc, n: PNode)
|
||||
proc genIfStmt(context: AsmProc, n: PNode) =
|
||||
context.elifStmtNum = 0
|
||||
let endIfLabel = ".endIf" & $context.ifStmtNum
|
||||
for i in 0 .. <n.sons.len:
|
||||
case n.sons[i].kind
|
||||
of nkElifBranch:
|
||||
let nextBranchLabel =
|
||||
".nextBranch" & $context.ifStmtNum & $context.elifStmtNum
|
||||
genIfCond(context, n.sons[i][0], context.getProcName() & nextBranchLabel)
|
||||
genProcBody(context, n.sons[i][1])
|
||||
codeBody.emit(opcJmp, context.getProcName() & endIfLabel)
|
||||
codeBody.addN(nextBranchLabel & ":")
|
||||
context.elifStmtNum.inc()
|
||||
of nkElse:
|
||||
genProcBody(context, n.sons[i][0])
|
||||
else:
|
||||
internalError("Unexpected node kind in if stmt: " & $n.sons[i].kind)
|
||||
codeBody.addN(endIfLabel & ":")
|
||||
|
||||
context.ifStmtNum.inc
|
||||
|
||||
proc genProcBody(context: AsmProc, n: PNode) =
|
||||
## Generates asm code for the body of a nkProcDef
|
||||
case n.kind
|
||||
of nkStmtList:
|
||||
for i in 0 .. <n.sons.len:
|
||||
genProcBody(context, n.sons[i])
|
||||
of nkAsgn:
|
||||
assert n.sons[0].kind == nkSym
|
||||
|
||||
case n.sons[0].sym.kind
|
||||
of skResult:
|
||||
# We map this to RAX.
|
||||
genAsgn(context, n.sons[1], regRAX)
|
||||
else:
|
||||
assert false
|
||||
of nkVarSection:
|
||||
genVars(context, n.sons[0])
|
||||
of nkIfStmt:
|
||||
genIfStmt(context, n)
|
||||
of nkBlockStmt:
|
||||
assert n.sons[0].kind == nkSym
|
||||
codeBody.addN(getBlockLabel(context, n.sons[0].sym) & ':')
|
||||
genProcBody(context, n.sons[1])
|
||||
of nkEmpty: discard
|
||||
else:
|
||||
assert false, $n.kind
|
||||
|
||||
proc newAsmProc(prc: PSym): AsmProc =
|
||||
new result
|
||||
result.stackSlotsCounter = 0
|
||||
result.owner = prc
|
||||
for i in low(result.code)..high(result.code):
|
||||
result.code[i] = ""
|
||||
|
||||
proc genProc*(m: BModule, prc: PSym) =
|
||||
var context = newAsmProc(prc)
|
||||
assert prc.ast.kind == nkProcDef
|
||||
let nameStr = getProcName(context)
|
||||
context.code[prcHeader].addN nameStr & ":"
|
||||
|
||||
# Add code for initialising the stack frame properly.
|
||||
# TODO: Only do this for debug mode.
|
||||
context.code[prcHeader].emit(opcPush, "rbp")
|
||||
context.code[prcHeader].emit(opcMov, "rbp", "rsp")
|
||||
|
||||
genProcBody(context, prc.ast[6])
|
||||
|
||||
# Reserve stack space for local variables
|
||||
var reserveSize = context.stackSlotsCounter*8
|
||||
if reserveSize != 0:
|
||||
if reserveSize mod 16 != 0: reserveSize.inc 8
|
||||
context.code[prcHeader].emit(opcSub, $regRSP, $reserveSize)
|
||||
|
||||
context.code[prcBody].emit(opcLeave)
|
||||
context.code[prcBody].emit(opcRet)
|
||||
m.asmSections[afsProcs].addN context.code[prcHeader]
|
||||
m.asmSections[afsProcs].addN context.code[prcBody]
|
||||
m.asmSections[afsGlobals].addIndentN "global " & nameStr
|
||||
|
||||
proc getAsmFilename(m: BModule): string =
|
||||
result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), "asm")
|
||||
var (head, tail) = splitPath(result)
|
||||
result = head / "asm_" & tail
|
||||
|
||||
proc writeModule*(m: BModule) =
|
||||
## Writes the specified module's assembly code to a file.
|
||||
let filename = getAsmFilename(m)
|
||||
echo(filename)
|
||||
var contents = ""
|
||||
contents.addIndentN("section .text")
|
||||
contents.addN(
|
||||
m.asmSections[afsGlobals] &
|
||||
m.asmSections[afsProcs])
|
||||
writeFile(filename, contents)
|
||||
|
||||
proc compileModule*(m: BModule) =
|
||||
let filename = getAsmFilename(m)
|
||||
execExternalProgram("yasm -f elf64 -g dwarf2 -o $1 $2 " %
|
||||
[filename.changeFileExt("o"), filename])
|
||||
addFileToLink(filename.changeFileExt(""))
|
||||
|
|
@ -15,7 +15,7 @@ import
|
|||
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
|
||||
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms,
|
||||
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
|
||||
semparallel
|
||||
semparallel, asmgen
|
||||
|
||||
when options.hasTinyCBackend:
|
||||
import tccgen
|
||||
|
|
@ -763,6 +763,10 @@ proc closureSetup(p: BProc, prc: PSym) =
|
|||
rdLoc(env.loc), getTypeDesc(p.module, env.typ))
|
||||
|
||||
proc genProcAux(m: BModule, prc: PSym) =
|
||||
if prc.info ?? "asmtest.nim":
|
||||
debug prc.ast
|
||||
asmgen.genProc(m, prc)
|
||||
return
|
||||
var p = newProc(prc, m)
|
||||
var header = genProcHeader(m, prc)
|
||||
var returnStmt: PRope = nil
|
||||
|
|
@ -1217,6 +1221,8 @@ proc rawNewModule(module: PSym, filename: string): BModule =
|
|||
result.forwardedProcs = @[]
|
||||
result.typeNodesName = getTempName()
|
||||
result.nimTypesName = getTempName()
|
||||
for i in low(result.asmSections)..high(result.asmSections):
|
||||
result.asmSections[i] = ""
|
||||
# no line tracing for the init sections of the system module so that we
|
||||
# don't generate a TFrame which can confuse the stack botton initialization:
|
||||
if sfSystemModule in module.flags:
|
||||
|
|
@ -1397,6 +1403,11 @@ proc writeModule(m: BModule, pending: bool) =
|
|||
|
||||
addFileToLink(cfilenoext)
|
||||
|
||||
# Check to see if there is asm code that we need to write and compile.
|
||||
if m.asmSections[afsProcs] != "":
|
||||
asmgen.writeModule(m)
|
||||
asmgen.compileModule(m)
|
||||
|
||||
proc updateCachedModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
let cfilenoext = changeFileExt(cfile, "")
|
||||
|
|
|
|||
|
|
@ -117,6 +117,13 @@ type
|
|||
extensionLoaders*: array['0'..'9', PRope] # special procs for the
|
||||
# OpenGL wrapper
|
||||
injectStmt*: PRope
|
||||
asmSections*: AsmFileSections ## assembly code generated for this module.
|
||||
## only applicable when the backend used
|
||||
## is asm.
|
||||
|
||||
AsmFileSection* = enum
|
||||
afsProcs, afsGlobals
|
||||
AsmFileSections* = array[AsmFileSection, string]
|
||||
|
||||
var
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue