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,
|
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
|
||||||
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms,
|
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms,
|
||||||
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
|
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
|
||||||
semparallel
|
semparallel, asmgen
|
||||||
|
|
||||||
when options.hasTinyCBackend:
|
when options.hasTinyCBackend:
|
||||||
import tccgen
|
import tccgen
|
||||||
|
|
@ -763,6 +763,10 @@ proc closureSetup(p: BProc, prc: PSym) =
|
||||||
rdLoc(env.loc), getTypeDesc(p.module, env.typ))
|
rdLoc(env.loc), getTypeDesc(p.module, env.typ))
|
||||||
|
|
||||||
proc genProcAux(m: BModule, prc: PSym) =
|
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 p = newProc(prc, m)
|
||||||
var header = genProcHeader(m, prc)
|
var header = genProcHeader(m, prc)
|
||||||
var returnStmt: PRope = nil
|
var returnStmt: PRope = nil
|
||||||
|
|
@ -1217,6 +1221,8 @@ proc rawNewModule(module: PSym, filename: string): BModule =
|
||||||
result.forwardedProcs = @[]
|
result.forwardedProcs = @[]
|
||||||
result.typeNodesName = getTempName()
|
result.typeNodesName = getTempName()
|
||||||
result.nimTypesName = 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
|
# 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:
|
# don't generate a TFrame which can confuse the stack botton initialization:
|
||||||
if sfSystemModule in module.flags:
|
if sfSystemModule in module.flags:
|
||||||
|
|
@ -1397,6 +1403,11 @@ proc writeModule(m: BModule, pending: bool) =
|
||||||
|
|
||||||
addFileToLink(cfilenoext)
|
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) =
|
proc updateCachedModule(m: BModule) =
|
||||||
let cfile = getCFile(m)
|
let cfile = getCFile(m)
|
||||||
let cfilenoext = changeFileExt(cfile, "")
|
let cfilenoext = changeFileExt(cfile, "")
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,13 @@ type
|
||||||
extensionLoaders*: array['0'..'9', PRope] # special procs for the
|
extensionLoaders*: array['0'..'9', PRope] # special procs for the
|
||||||
# OpenGL wrapper
|
# OpenGL wrapper
|
||||||
injectStmt*: PRope
|
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
|
var
|
||||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
|
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue