From ddaea3b70456f6d62a3b073b19d03c011ba7898d Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Mon, 27 Nov 2023 18:34:32 +0300 Subject: [PATCH 01/29] Asm (gcc extended asm) for nir --- compiler/nir/ast2ir.nim | 7 + compiler/nir/cir.nim | 67 ++++++++ compiler/nir/genasm.nim | 354 ++++++++++++++++++++++++++++++++++++++ compiler/nir/nirinsts.nim | 9 + compiler/nir/nirvm.nim | 2 + 5 files changed, 439 insertions(+) create mode 100644 compiler/nir/genasm.nim diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 51dfcdb094516..808d72c7ff6a0 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2517,6 +2517,8 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = else: genCall c, n, d +include genasm + proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags @@ -2560,6 +2562,11 @@ proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = unused(c, n, d) genWhile(c, n) of nkBlockExpr, nkBlockStmt: genBlock(c, n, d) + of nkAsmStmt: + if c.prc == nil: + genGlobalAsm(c, n) + else: + genInlineAsm(c, n) of nkReturnStmt: genReturn(c, n) of nkRaiseStmt: genRaise(c, n) of nkBreakStmt: genBreak(c, n) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 01f9c4c9add7f..59684b7560cbb 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -48,6 +48,7 @@ type CaseKeyword = "case " DefaultKeyword = "default:" BreakKeyword = "break" + AsmKeyword = "__asm__ " NullPtr = "nullptr" IfNot = "if (!(" ReturnKeyword = "return " @@ -812,6 +813,72 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of ForeignProcDecl: genProcDecl c, t, n, true of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc: c.add "cannot interpret: " & $t[n].kind + + of AsmGlobal: + c.add AsmKeyword + c.add ParLe + c.add NewLine + for ch in sons(t, n): + if t[ch].kind == StrVal: + let s = c.m.lit.strings[t[ch].litId] + var left = 0 + for j in 0..s.high: + if s[j] == '\n': + c.add makeCString(s[left..j]) + c.add NewLine + left = j + 1 + else: + gen c, t, ch + + c.add ParRi + c.add Semicolon + c.add NewLine + of Asm: + c.add AsmKeyword + c.add ParLe + gen c, t, n.firstSon #asmTemplate + for ch in sonsFrom1(t, n): + c.add Colon + gen c, t, ch + c.add NewLine + c.add ParRi + c.add Semicolon + c.add NewLine + of AsmTemplate: + c.add NewLine + for i in sons(t, n): + let s = c.m.lit.strings[t[i].litId] + var left = 0 + for j in 0..s.high: + if s[j] == '\n': + # separate every '\n' + # into different string like: + # " mov %1, %0\n" + # " add $1, %0\n" + # to produce more readable code + c.add makeCString(s[left..j]) + c.add NewLine + left = j + 1 + of AsmOutputOperand, AsmInputOperand: + let (dest, asmSymbolicName, constraint) = sons3(t, n) + let + symbolicName = c.m.lit.strings[t[asmSymbolicName].litId] + constraintStr = c.m.lit.strings[t[constraint].litId] + + if symbolicName != "": + c.add BracketLe + c.add '"' & symbolicName & '"' + c.add BracketRi + + c.add '"' & constraintStr & '"' + gen c, t, dest + of AsmInjectExpr: + c.add ParLe + gen c, t, n.firstSon + c.add ParRi + of AsmClobber: + let clobber = t[n.firstSon] + c.add c.m.lit.strings[clobber.litId] const Prelude = """ diff --git a/compiler/nir/genasm.nim b/compiler/nir/genasm.nim new file mode 100644 index 0000000000000..6657010d07311 --- /dev/null +++ b/compiler/nir/genasm.nim @@ -0,0 +1,354 @@ +# generates Asm stategments like this: +# Asm { +# AsmTemplate { +# Some asm code +# SymUse nimInlineVar # `a` +# Some asm code +# } +# AsmOutputOperand { +# # [asmSymbolicName] constraint (nimVariableName) +# AsmInjectExpr {symUse nimVariableName} # for output it have only one sym (lvalue) +# asmSymbolicName # default: "" +# constraint +# } +# AsmInputOperand { +# # [asmSymbolicName] constraint (nimExpr) +# AsmInjectExpr {symUse nimVariableName} # (rvalue) +# asmSymbolicName # default: "" +# constraint +# } +# AsmClobber { +# "clobber" +# } + +# it can be useful for better asm analysis and +# easy to use in all nim targets + +type + Det = enum + AsmTemplate + SymbolicName + InjectExpr + Constraint + Clobber + Delimiter + + AsmValKind = enum + StrVal + SymVal + NodeVal + EmptyVal + + AsmVal = object + case kind: AsmValKind + of StrVal: + s: string + of SymVal: + sym: SymId + of NodeVal: + n: PNode + of EmptyVal: + discard + + ParsedAsmChunk = tuple[sec: int, val: AsmVal, det: Det] + ParsedAsm = seq[ParsedAsmChunk] + +const + asmSections = [ + Opcode.AsmTemplate, + AsmOutputOperand, + AsmInputOperand, + AsmClobber + ] + +template createSectionItem: untyped = + prepare(c.code, info, asmSections[sec]) + +template closeItem: untyped = + patch(c.code, pos) + +proc toVal(n: PNode): AsmVal = + AsmVal(kind: NodeVal, n: n) + +proc toVal(s: string): AsmVal = + AsmVal(kind: StrVal, s: s) + +proc toVal(s: SymId): AsmVal = + AsmVal(kind: SymVal, sym: s) + +proc empty(): AsmVal = + AsmVal(kind: EmptyVal) + +# proc toVal() + +iterator parseAsm(c: var ProcCon, n: PNode): ParsedAsmChunk = + template addCaptured: untyped = + yield ( + sec, + captured.toVal, + det + ) + captured = "" + + template maybeAddCaptured: untyped = + if captured != "": + addCaptured() + + var sec = 0 + var det: Det = AsmTemplate + var left = 0 + var captured = "" + + # handling comments + var + inComment = false # current char in comment(note: comment chars is skipped) + isLineComment = false + foundCommentStartSym = false + foundCommentEndSym = false + + # helper debug info + + for it in n.sons: + case it.kind + of nkStrLit..nkTripleStrLit: + let s = it.strVal + + for i in 0..s.high: + + # Comments + if sec > 0 and foundCommentStartSym: + # "/?" + if s[i] == '/': + # "//" + inComment = true + isLineComment = true + elif s[i] == '*': + # "/*" + inComment = true + isLineComment = false + foundCommentStartSym = false # updates it + + if sec > 0 and not foundCommentStartSym and s[i] == '*': + #"(!/)*" + foundCommentEndSym = true + elif sec > 0 and foundCommentEndSym: # "*?" + if s[i] == '/': # "*/" + inComment = false + # delete captured '/' + captured = "" + continue + foundCommentEndSym = false + if sec > 0 and s[i] == '/': # '/' + foundCommentStartSym = true + if sec > 0 and s[i] == '\n' and inComment: + if not isLineComment: # /* comment \n + raiseAssert """expected "*/", not "*""" & s[i] & """" in asm operand""" + inComment = false + # delete captured '/' + captured = "" + continue + if inComment: + # skip commented syms + continue + + + + case s[i]: + of ':': + if sec == 0: # det == AsmTemplate + yield ( + sec, + s[left..i - 1].toVal, + det + ) + + inc sec + # inc det + left = i + 1 + captured = "" + + if sec in 1..2: + # default det for operands + det = Constraint + elif sec == 3: + det = Clobber + + of '[': + # start of asm symbolic name + det = SymbolicName + + of ']': + if det != SymbolicName: + raiseAssert "expected: ']'" + + addCaptured() + + det = Constraint + # s[capturedStart .. i - 1] + + of '(': + addCaptured() # add asm constraint + det = InjectExpr + + of ')': + if det != InjectExpr: + raiseAssert "expected: ')'" + + maybeAddCaptured() + + elif sec > 0 and s[i] == ',': + if sec in 1..2: + det = Constraint + + if sec == 3: + maybeAddCaptured() + + yield ( + sec, + empty(), + Delimiter + ) + + elif ( + sec > 0 and + det in { + SymbolicName, + Constraint, + InjectExpr, + Clobber + } and + s[i] notin {' ', '\n', '\t'} + ): captured.add s[i] + + + else: discard + + else: + maybeAddCaptured() + + yield ( + sec, + if it.kind == nkSym: + toSymId(c, it.sym).toVal + else: + it.toVal, + det + ) + + left = 0 + + if sec == 0: + # : not specified + yield ( + sec, + n[^1].strVal.toVal, + det + ) + elif sec > 2: + maybeAddCaptured() + +proc genInlineAsm(c: var ProcCon; n: PNode) = + template createInstr(sec: int): untyped = + prepare(c.code, info, asmSections[sec]) + + template createInstr(instr: Opcode): untyped = + prepare(c.code, info, instr) + + template endInstr: untyped = + patch(c.code, pos) + + template maybeEndOperand(): untyped = + if inInjectExpr: + # end of old operand + endInstr() # AsmInjectExpr node + c.code.addStrVal c.lit.strings, info, asmSymbolicName + c.code.addStrVal c.lit.strings, info, constraint + + pos = oldPos + + #default operand info + inInjectExpr = false + asmSymbolicName = "" + constraint = "" + + let info = toLineInfo(c, n.info) + build c.code, info, Asm: + var + pos = createInstr(AsmTemplate) + oldPos = pos + + oldSec = 0 + # operands + asmSymbolicName = "" + constraint = "" + inInjectExpr = false + + for i in parseAsm(c, n): + when true: + echo i + + if i.sec != oldSec:# or i.sec == 0: + # new sec + maybeEndOperand() + + endInstr() + pos = createInstr(i.sec) + + case i.det: + of AsmTemplate: + # it's tmp code, template can contains nodes + c.code.addStrVal c.lit.strings, info, i.val.s + + of SymbolicName: + asmSymbolicName = i.val.s + of Constraint: + let s = i.val.s + if s[0] != '"' or s[^1] != '"': + raiseAssert "constraint must be started or ended by " & '"' + constraint = s[1..^2] + of InjectExpr: + # (`dst`) + if not inInjectExpr: + oldPos = pos + pos = createInstr(AsmInjectExpr) + + case i.val.kind: + of SymVal: + c.code.addSymUse info, i.val.sym + of StrVal: + c.code.addStrVal c.lit.strings, info, i.val.s + of NodeVal: + raiseAssert "unsupported" + of EmptyVal: raiseAssert "never" + + inInjectExpr = true + + of Delimiter: + maybeEndOperand() + + endInstr() + pos = createInstr(i.sec) + + of Clobber: + let s = i.val.s + if s[0] != '"' or s[^1] != '"': + raiseAssert "constraint must be started or ended by " & '"' + c.code.addStrVal c.lit.strings, info, s[1..^2] + + oldSec = i.sec + + maybeEndOperand() + + endInstr() + +proc genGlobalAsm(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + build c.code, info, AsmGlobal: + for i in n: + case i.kind + of nkStrLit..nkTripleStrLit: + c.code.addStrVal c.lit.strings, info, i.strVal + of nkSym: + c.code.addSymUse info, toSymId(c, i.sym) + else: + gen(c, i) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 6cffc1a8938d4..4babddb40f487 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -108,6 +108,15 @@ type ObjConv, TestOf, Emit, + + AsmGlobal # top-level basic asm. Better to use extended asm if it possible + Asm, # extended asm + AsmTemplate, + AsmInjectExpr, # like cexpr in gcc + AsmOutputOperand, + AsmInputOperand, + AsmClobber, + ProcDecl, ForeignProcDecl, PragmaPair diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim index faa7a1fb7eb3f..dde9b7eb85625 100644 --- a/compiler/nir/nirvm.nim +++ b/compiler/nir/nirvm.nim @@ -702,6 +702,8 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla recurse TestOfM of Emit: raiseAssert "cannot interpret: Emit" + of AsmGlobal .. AsmClobber: + raiseAssert "cannot interpret: Asm" of ProcDecl: var c2 = Preprocessing(u: c.u, thisModule: c.thisModule) let sym = t[n.firstSon].symId From 73eb391d4772192b53c4175cac30b5dbff1e2580 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Mon, 27 Nov 2023 18:39:08 +0300 Subject: [PATCH 02/29] Update genasm.nim --- compiler/nir/genasm.nim | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/nir/genasm.nim b/compiler/nir/genasm.nim index 6657010d07311..f0592b39dbc8c 100644 --- a/compiler/nir/genasm.nim +++ b/compiler/nir/genasm.nim @@ -106,8 +106,6 @@ iterator parseAsm(c: var ProcCon, n: PNode): ParsedAsmChunk = foundCommentStartSym = false foundCommentEndSym = false - # helper debug info - for it in n.sons: case it.kind of nkStrLit..nkTripleStrLit: @@ -284,10 +282,10 @@ proc genInlineAsm(c: var ProcCon; n: PNode) = inInjectExpr = false for i in parseAsm(c, n): - when true: + when false: echo i - if i.sec != oldSec:# or i.sec == 0: + if i.sec != oldSec: # new sec maybeEndOperand() @@ -332,7 +330,7 @@ proc genInlineAsm(c: var ProcCon; n: PNode) = of Clobber: let s = i.val.s if s[0] != '"' or s[^1] != '"': - raiseAssert "constraint must be started or ended by " & '"' + raiseAssert "clobber must be started or ended by " & '"' c.code.addStrVal c.lit.strings, info, s[1..^2] oldSec = i.sec From 73f9b60104ffe27262d5e121d53c73b48f59485e Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Wed, 29 Nov 2023 21:54:40 +0300 Subject: [PATCH 03/29] Asm parsing now working on backends (WIP( --- compiler/nir/gcc_extended_asm.nim | 314 ++++++++++++++++++++++++++++++ compiler/nir/nirinsts.nim | 9 +- 2 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 compiler/nir/gcc_extended_asm.nim diff --git a/compiler/nir/gcc_extended_asm.nim b/compiler/nir/gcc_extended_asm.nim new file mode 100644 index 0000000000000..a424f84fbfa28 --- /dev/null +++ b/compiler/nir/gcc_extended_asm.nim @@ -0,0 +1,314 @@ +# generates Asm stategments like this: +# Asm { +# AsmTemplate { +# Some asm code +# SymUse nimInlineVar # `a` +# Some asm code +# } +# AsmOutputOperand { +# # [asmSymbolicName] constraint (nimVariableName) +# AsmInjectExpr {symUse nimVariableName} # for output it have only one sym (lvalue) +# asmSymbolicName # default: "" +# constraint +# } +# AsmInputOperand { +# # [asmSymbolicName] constraint (nimExpr) +# AsmInjectExpr {symUse nimVariableName} # (rvalue) +# asmSymbolicName # default: "" +# constraint +# } +# AsmClobber { +# "clobber" +# } + +# it can be useful for better asm analysis and +# easy to use in all nim targets +import nirinsts, nirtypes +import std / assertions +import .. / ic / bitabs + +type + Det = enum + AsmTemplate + SymbolicName + InjectExpr + Constraint + Clobber + Delimiter + + AsmValKind = enum + StrVal + # SymVal + NodeVal + EmptyVal + + AsmVal = object + case kind: AsmValKind + of StrVal: + s: string + # of SymVal: + # sym: SymId + of NodeVal: + n: NodePos + of EmptyVal: + discard + + AsmToken = tuple[sec: int, val: AsmVal, det: Det] + + AsmNodeKind* = enum + AsmTemplate + AsmOutputOperand + AsmInputOperand + AsmClobber + + AsmInjectExpr + AsmStrVal + + GccAsmNode* = ref object + case kind: AsmNodeKind + of AsmTemplate: + instrs: seq[GccAsmNode] + of AsmOutputOperand, AsmInputOperand: + symbolicName: string + constraint: string + injectExpr: GccAsmNode + of AsmClobber: + clobber: string + of AsmStrVal: + s: string + of AsmInjectExpr: + n: NodePos + + +proc toVal(n: NodePos): AsmVal = + AsmVal(kind: NodeVal, n: n) + +proc toVal(s: string): AsmVal = + AsmVal(kind: StrVal, s: s) + +# proc toVal(s: SymId): AsmVal = +# AsmVal(kind: SymVal, sym: s) + +proc empty(): AsmVal = + AsmVal(kind: EmptyVal) + +proc toNode(val: AsmVal): GccAsmNode = + # get str node or + case val.kind: + of StrVal: GccAsmNode(kind: AsmStrVal, s: val.s) + of NodeVal: GccAsmNode(kind: AsmInjectExpr, n: val.n) + else: raiseAssert"unsupported val" + +proc emptyNode(kind: AsmNodeKind): GccAsmNode = + GccAsmNode(kind: kind) + +iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = + template addCaptured: untyped = + yield ( + sec, + captured.toVal, + det + ) + captured = "" + + template maybeAddCaptured: untyped = + if captured != "": + addCaptured() + + var sec = 0 + var det: Det = AsmTemplate + var left = 0 + var captured = "" + + # handling comments + var + inComment = false # current char in comment(note: comment chars is skipped) + isLineComment = false + foundCommentStartSym = false + foundCommentEndSym = false + + for ch in sons(t, n): + case t[ch].kind + of Verbatim: + let s = ""#it.strVal + + for i in 0..s.high: + + # Comments + if sec > 0 and foundCommentStartSym: + # "/?" + if s[i] == '/': + # "//" + inComment = true + isLineComment = true + elif s[i] == '*': + # "/*" + inComment = true + isLineComment = false + foundCommentStartSym = false # updates it + + if sec > 0 and not foundCommentStartSym and s[i] == '*': + #"(!/)*" + foundCommentEndSym = true + elif sec > 0 and foundCommentEndSym: # "*?" + if s[i] == '/': # "*/" + inComment = false + # delete captured '/' + captured = "" + continue + foundCommentEndSym = false + if sec > 0 and s[i] == '/': # '/' + foundCommentStartSym = true + if sec > 0 and s[i] == '\n' and inComment: + if not isLineComment: # /* comment \n + raiseAssert """expected "*/", not "*""" & s[i] & """" in asm operand""" + inComment = false + # delete captured '/' + captured = "" + continue + if inComment: + # skip commented syms + continue + + + + case s[i]: + of ':': + if sec == 0: # det == AsmTemplate + yield ( + sec, + s[left..i - 1].toVal, + det + ) + + inc sec + # inc det + left = i + 1 + captured = "" + + if sec in 1..2: + # default det for operands + det = Constraint + elif sec == 3: + det = Clobber + + of '[': + # start of asm symbolic name + det = SymbolicName + + of ']': + if det != SymbolicName: + raiseAssert "expected: ']'" + + addCaptured() + + det = Constraint + # s[capturedStart .. i - 1] + + of '(': + addCaptured() # add asm constraint + det = InjectExpr + + of ')': + if det != InjectExpr: + raiseAssert "expected: ')'" + + maybeAddCaptured() + + elif sec > 0 and s[i] == ',': + if sec in 1..2: + det = Constraint + + if sec == 3: + maybeAddCaptured() + + yield ( + sec, + empty(), + Delimiter + ) + + elif ( + sec > 0 and + det in { + SymbolicName, + Constraint, + InjectExpr, + Clobber + } and + s[i] notin {' ', '\n', '\t'} + ): captured.add s[i] + + + else: discard + + else: + maybeAddCaptured() + + yield ( + sec, + ch.toVal, + det + ) + + left = 0 + + if sec == 0: + # : not specified + yield ( + sec, + lit.strings[t[lastSon(t, n)].litId].toVal, + det + ) + elif sec > 2: + maybeAddCaptured() + +const + sections = [ + AsmNodeKind.AsmTemplate, + AsmOutputOperand, + AsmInputOperand, + AsmClobber + ] + +iterator parseGccAsm*(t: Tree, n: NodePos; lit: Literals): GccAsmNode = + var + oldSec = 0 + curr = emptyNode(AsmTemplate) + inInjectExpr = false + + template initNextNode: untyped = + curr = emptyNode(sections[i.sec]) + + for i in asmTokens(t, n, lit): + if i.sec != oldSec: + # current node fully filled + yield curr + initNextNode() + + case i.det: + of Delimiter: + yield curr + initNextNode() + + of AsmTemplate: + curr.instrs.add i.val.toNode + + of SymbolicName: + curr.symbolicName = i.val.s + of Constraint: + let s = i.val.s + if s[0] != '"' or s[^1] != '"': + raiseAssert "constraint must be started and ended by " & '"' + curr.constraint = s[1..^2] + of InjectExpr: + # only one inject expr for now + curr.injectExpr = i.val.toNode + + of Clobber: + let s = i.val.s + if s[0] != '"' or s[^1] != '"': + raiseAssert "clobber must be started and ended by " & '"' + curr.clobber = s[1..^2] + + oldSec = i.sec diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 4babddb40f487..6341d15dee6f6 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -108,8 +108,10 @@ type ObjConv, TestOf, Emit, + EmitTarget, + Verbatim - AsmGlobal # top-level basic asm. Better to use extended asm if it possible + AsmGlobal, # top-level basic asm. Better to use extended asm if it possible Asm, # extended asm AsmTemplate, AsmInjectExpr, # like cexpr in gcc @@ -254,6 +256,11 @@ proc nextChild(tree: Tree; pos: var int) {.inline.} = proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos) template firstSon*(n: NodePos): NodePos = NodePos(n.int+1) +proc lastSon*(tree: Tree; n: NodePos): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + NodePos last template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2) From a4ddb3b305cadb87a374b0059ea4f178bc65f22b Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Wed, 29 Nov 2023 23:36:42 +0300 Subject: [PATCH 04/29] Emit targets --- compiler/nir/ast2ir.nim | 19 +++++++++++++------ compiler/nir/cir.nim | 4 ++++ compiler/nir/nirinsts.nim | 10 ++++++++++ compiler/nir/nirvm.nim | 4 +++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 808d72c7ff6a0..056287a035f21 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2517,7 +2517,18 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = else: genCall c, n, d -include genasm +# include genasm + +proc genAsm(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + build c.code, info, Emit: + c.code.addEmitTarget info, Asm + + + # if c.prc == nil: + # genGlobalAsm(c, n) + # else: + # genInlineAsm(c, n) proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = when defined(nimCompilerStacktraceHints): @@ -2562,11 +2573,7 @@ proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = unused(c, n, d) genWhile(c, n) of nkBlockExpr, nkBlockStmt: genBlock(c, n, d) - of nkAsmStmt: - if c.prc == nil: - genGlobalAsm(c, n) - else: - genInlineAsm(c, n) + of nkAsmStmt: genAsm(c, n) of nkReturnStmt: genReturn(c, n) of nkRaiseStmt: genRaise(c, n) of nkBreakStmt: genBreak(c, n) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 59684b7560cbb..feda9694ccbe5 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -879,6 +879,10 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of AsmClobber: let clobber = t[n.firstSon] c.add c.m.lit.strings[clobber.litId] + of Verbatim: + discard + of EmitTarget: + discard const Prelude = """ diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 6341d15dee6f6..6719881e71088 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -132,6 +132,10 @@ type DllImport, DllExport, ObjExport + + EmitTargetKind* = enum + Asm + Code const LastAtomicValue = GotoLoop @@ -423,6 +427,9 @@ proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) = proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) = t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info) +proc addEmitTarget*(t: var Tree; info: PackedLineInfo; x: EmitTargetKind) = + t.nodes.add Instr(x: toX(EmitTarget, uint32(x)), info: info) + proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = buildTyped t, info, NumberConv, typ: t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info) @@ -507,6 +514,9 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl of SymUse: r.add "SymUse " r.add localName(SymId t[pos].operand) + of EmitTarget: + r.add "EmitTarget " + r.add $cast[EmitTargetKind](t[pos].operand) of PragmaId: r.add $cast[PragmaKey](t[pos].operand) of Typed: diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim index dde9b7eb85625..a8d97c926c5af 100644 --- a/compiler/nir/nirvm.nim +++ b/compiler/nir/nirvm.nim @@ -700,10 +700,12 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla recurse ObjConvM of TestOf: recurse TestOfM - of Emit: + of Emit, EmitTarget: raiseAssert "cannot interpret: Emit" of AsmGlobal .. AsmClobber: raiseAssert "cannot interpret: Asm" + of Verbatim: + raiseAssert "cannot interpret: Verbatim" of ProcDecl: var c2 = Preprocessing(u: c.u, thisModule: c.thisModule) let sym = t[n.firstSon].symId From 03af260aae16223fb4a7d0028b5412050e011964 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Wed, 29 Nov 2023 23:51:10 +0300 Subject: [PATCH 05/29] Remove asm instrs --- compiler/nir/ast2ir.nim | 2 -- compiler/nir/cir.nim | 65 --------------------------------------- compiler/nir/nirinsts.nim | 10 +----- compiler/nir/nirvm.nim | 2 -- 4 files changed, 1 insertion(+), 78 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 056287a035f21..8bb7332f9c87b 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2517,8 +2517,6 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = else: genCall c, n, d -# include genasm - proc genAsm(c: var ProcCon; n: PNode) = let info = toLineInfo(c, n.info) build c.code, info, Emit: diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index feda9694ccbe5..7312effa4dc09 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -814,71 +814,6 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc: c.add "cannot interpret: " & $t[n].kind - of AsmGlobal: - c.add AsmKeyword - c.add ParLe - c.add NewLine - for ch in sons(t, n): - if t[ch].kind == StrVal: - let s = c.m.lit.strings[t[ch].litId] - var left = 0 - for j in 0..s.high: - if s[j] == '\n': - c.add makeCString(s[left..j]) - c.add NewLine - left = j + 1 - else: - gen c, t, ch - - c.add ParRi - c.add Semicolon - c.add NewLine - of Asm: - c.add AsmKeyword - c.add ParLe - gen c, t, n.firstSon #asmTemplate - for ch in sonsFrom1(t, n): - c.add Colon - gen c, t, ch - c.add NewLine - c.add ParRi - c.add Semicolon - c.add NewLine - of AsmTemplate: - c.add NewLine - for i in sons(t, n): - let s = c.m.lit.strings[t[i].litId] - var left = 0 - for j in 0..s.high: - if s[j] == '\n': - # separate every '\n' - # into different string like: - # " mov %1, %0\n" - # " add $1, %0\n" - # to produce more readable code - c.add makeCString(s[left..j]) - c.add NewLine - left = j + 1 - of AsmOutputOperand, AsmInputOperand: - let (dest, asmSymbolicName, constraint) = sons3(t, n) - let - symbolicName = c.m.lit.strings[t[asmSymbolicName].litId] - constraintStr = c.m.lit.strings[t[constraint].litId] - - if symbolicName != "": - c.add BracketLe - c.add '"' & symbolicName & '"' - c.add BracketRi - - c.add '"' & constraintStr & '"' - gen c, t, dest - of AsmInjectExpr: - c.add ParLe - gen c, t, n.firstSon - c.add ParRi - of AsmClobber: - let clobber = t[n.firstSon] - c.add c.m.lit.strings[clobber.litId] of Verbatim: discard of EmitTarget: diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 6719881e71088..d7f981be3eac5 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -109,15 +109,7 @@ type TestOf, Emit, EmitTarget, - Verbatim - - AsmGlobal, # top-level basic asm. Better to use extended asm if it possible - Asm, # extended asm - AsmTemplate, - AsmInjectExpr, # like cexpr in gcc - AsmOutputOperand, - AsmInputOperand, - AsmClobber, + Verbatim, ProcDecl, ForeignProcDecl, diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim index a8d97c926c5af..5ae2246879898 100644 --- a/compiler/nir/nirvm.nim +++ b/compiler/nir/nirvm.nim @@ -702,8 +702,6 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla recurse TestOfM of Emit, EmitTarget: raiseAssert "cannot interpret: Emit" - of AsmGlobal .. AsmClobber: - raiseAssert "cannot interpret: Asm" of Verbatim: raiseAssert "cannot interpret: Verbatim" of ProcDecl: From 545d35adcecb8f7e66a32706d751a86736ea1817 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Fri, 1 Dec 2023 17:23:57 +0300 Subject: [PATCH 06/29] Update nirinsts.nim --- compiler/nir/nirinsts.nim | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index d7f981be3eac5..5057e0358dd88 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -40,7 +40,9 @@ type Goto, CheckedGoto, LoopLabel, - GotoLoop, # last atom + GotoLoop, + EmitTarget, + Verbatim, # last atom ModuleSymUse, # `"module".x` @@ -108,8 +110,6 @@ type ObjConv, TestOf, Emit, - EmitTarget, - Verbatim, ProcDecl, ForeignProcDecl, @@ -130,7 +130,7 @@ type Code const - LastAtomicValue = GotoLoop + LastAtomicValue = Verbatim OpcodeBits = 8'u32 OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 @@ -355,7 +355,7 @@ proc immediateVal*(ins: Instr): int {.inline.} = result = cast[int](ins.operand) proc litId*(ins: Instr): LitId {.inline.} = - assert ins.kind in {StrVal, IntVal} + assert ins.kind in {StrVal, IntVal, Verbatim} result = LitId(ins.operand) @@ -500,6 +500,11 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl r.add $integers[LitId t[pos].operand] of StrVal: escapeToNimLit(strings[LitId t[pos].operand], r) + of Verbatim: + r.add """Verbatim """""" & '\n' + # r.add verbatims[LitId t[pos].operand] + r.add '\n' & """"""""" + of SymDef: r.add "SymDef " r.add localName(SymId t[pos].operand) From 5cefc658eebee1f5328237a08876057189272b3a Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Fri, 1 Dec 2023 18:37:20 +0300 Subject: [PATCH 07/29] verbatims --- compiler/ic/rodfiles.nim | 1 + compiler/nir/nirc.nim | 2 +- compiler/nir/nirinsts.nim | 21 ++++++++++++--------- compiler/nir/nirtypes.nim | 1 + compiler/nir/nirvm.nim | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 968bf255f4301..a46ddca227850 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -73,6 +73,7 @@ type versionSection configSection stringsSection + verbatimsSection checkSumsSection depsSection numbersSection diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim index a2cf69988abed..fb1bd7cf8892e 100644 --- a/compiler/nir/nirc.nim +++ b/compiler/nir/nirc.nim @@ -15,7 +15,7 @@ import nirinsts, nirtypes, nirlineinfos, nirfiles, cir proc view(filename: string) = let m = load(filename) var res = "" - allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res + allTreesToString m.code, m.lit.strings, m.lit.verbatims, m.lit.numbers, m.symnames, res res.add "\n# TYPES\n" nirtypes.toString res, m.types echo res diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 5057e0358dd88..88c2add4df221 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -355,7 +355,7 @@ proc immediateVal*(ins: Instr): int {.inline.} = result = cast[int](ins.operand) proc litId*(ins: Instr): LitId {.inline.} = - assert ins.kind in {StrVal, IntVal, Verbatim} + assert ins.kind in {StrVal, Verbatim, IntVal} result = LitId(ins.operand) @@ -436,6 +436,9 @@ proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) = t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info) +proc addVerbatim*(t: var Tree; verbatims: var BiTable[string]; info: PackedLineInfo; s: string) = + t.nodes.add Instr(x: toX(Verbatim, uint32(verbatims.getOrIncl(s))), info: info) + proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) = buildTyped t, info, NumberConv, typ: t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info) @@ -485,9 +488,10 @@ template localName(s: SymId): string = proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s) proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s) -proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64]; +proc toString*(t: Tree; pos: NodePos; strings, verbatims: BiTable[string], integers: BiTable[int64]; names: SymNames; r: var string; nesting = 0) = + const tripleQuote = """"""""" if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: r.add ' ' @@ -501,10 +505,9 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl of StrVal: escapeToNimLit(strings[LitId t[pos].operand], r) of Verbatim: - r.add """Verbatim """""" & '\n' - # r.add verbatims[LitId t[pos].operand] - r.add '\n' & """"""""" - + r.add "Verbatim " & tripleQuote & '\n' + r.add verbatims[LitId t[pos].operand] + r.add '\n' & tripleQuote of SymDef: r.add "SymDef " r.add localName(SymId t[pos].operand) @@ -538,17 +541,17 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl r.add "{\n" for i in 0..<(nesting+1)*2: r.add ' ' for p in sons(t, pos): - toString t, p, strings, integers, names, r, nesting+1 + toString t, p, strings, verbatims, integers, names, r, nesting+1 r.add "\n" for i in 0.. Date: Sat, 2 Dec 2023 13:07:32 +0300 Subject: [PATCH 08/29] gcc asm (simple) --- compiler/nir/ast2ir.nim | 19 +++++++++++----- compiler/nir/cir.nim | 46 +++++++++++++++++++++++++++++++++++---- compiler/nir/nirfiles.nim | 6 +++++ compiler/nir/nirinsts.nim | 1 + compiler/nir/nirvm.nim | 2 +- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 8bb7332f9c87b..8d989dc345c82 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2517,16 +2517,23 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = else: genCall c, n, d +template genEmitCode(c: var ProcCon; n: PNode) = + build c.code, info, EmitCode: + for i in n: + case i.kind: + of nkStrLit..nkTripleStrLit: + c.code.addVerbatim c.lit.verbatims, info, i.strVal + of nkSym: + c.code.addSymUse info, toSymId(c, i.sym) + else: + gen(c, i) + proc genAsm(c: var ProcCon; n: PNode) = let info = toLineInfo(c, n.info) build c.code, info, Emit: c.code.addEmitTarget info, Asm - - - # if c.prc == nil: - # genGlobalAsm(c, n) - # else: - # genInlineAsm(c, n) + genEmitCode c, n + proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = when defined(nimCompilerStacktraceHints): diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 7312effa4dc09..257bfae72b9d5 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -555,6 +555,36 @@ template moveToDataSection(body: untyped) = c.data.add c.code[i] setLen c.code, oldLen +proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = + c.add AsmKeyword + c.add ParLe + c.add NewLine + + var sec = 0 + template maybeAddQuotes: untyped = + if sec == 0: c.add """"""" + template maybeAddQuoted(s: string): untyped = + maybeAddQuotes + c.add s + maybeAddQuotes + + for i in sons(t, n): + if t[i].kind == Verbatim: + let s = c.m.lit.verbatims[t[i].litId] + var left = 0 + for j in 0..s.high: + if s[j] == '\n': + maybeAddQuoted s[left..j-1] & (if sec == 0: r"\n" else: "") + c.add NewLine + left = j + 1 + elif s[j] == ':': inc sec + + maybeAddQuoted s[left..^1] + else: c.gen(t, i) + c.add NewLine + c.add ParRi + c.add Semicolon + proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case t[n].kind of Nop: @@ -808,15 +838,23 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of NumberConv: genNumberConv c, t, n of CheckedObjConv: binaryop "" of ObjConv: binaryop "" - of Emit: raiseAssert "cannot interpret: Emit" of ProcDecl: genProcDecl c, t, n, false of ForeignProcDecl: genProcDecl c, t, n, true of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc: c.add "cannot interpret: " & $t[n].kind - of Verbatim: - discard - of EmitTarget: + c.add c.m.lit.verbatims[t[n].litId] + of Emit: + let (targetRaw, code) = sons2(t, n) + let target = cast[EmitTargetKind](t[targetRaw].rawOperand) + + case target: + of Asm: + genGccAsm(c, t, code) + of Code: + raiseAssert"not supported" + + of EmitTarget, EmitCode: discard const diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim index cd5a79f06e444..b7dcba82fcf16 100644 --- a/compiler/nir/nirfiles.nim +++ b/compiler/nir/nirfiles.nim @@ -29,6 +29,9 @@ proc load*(filename: string): NirModule = r.loadSection stringsSection r.load result.lit.strings + r.loadSection verbatimsSection + r.load result.lit.verbatims + r.loadSection numbersSection r.load result.lit.numbers @@ -58,6 +61,9 @@ proc store*(m: NirModule; outp: string) = r.storeSection stringsSection r.store m.lit.strings + r.storeSection verbatimsSection + r.store m.lit.verbatims + r.storeSection numbersSection r.store m.lit.numbers diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 88c2add4df221..e229b5ce1fc50 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -110,6 +110,7 @@ type ObjConv, TestOf, Emit, + EmitCode, ProcDecl, ForeignProcDecl, diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim index 68e885dbbd3a6..140b962db81d6 100644 --- a/compiler/nir/nirvm.nim +++ b/compiler/nir/nirvm.nim @@ -700,7 +700,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla recurse ObjConvM of TestOf: recurse TestOfM - of Emit, EmitTarget: + of Emit, EmitTarget, EmitCode: raiseAssert "cannot interpret: Emit" of Verbatim: raiseAssert "cannot interpret: Verbatim" From c0ab5801a7ecc649741a7fb14a1053f9ee684f23 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sat, 2 Dec 2023 13:49:10 +0300 Subject: [PATCH 09/29] fix lastSon --- compiler/nir/nirinsts.nim | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index e229b5ce1fc50..4036b20067caa 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -254,10 +254,17 @@ proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos) template firstSon*(n: NodePos): NodePos = NodePos(n.int+1) proc lastSon*(tree: Tree; n: NodePos): NodePos = - var pos = n.int + var + pos = n.int + oldPos = pos + assert tree.nodes[pos].kind > LastAtomicValue let last = pos + tree.nodes[pos].rawSpan - NodePos last + inc pos + while pos < last: + oldPos = pos + nextChild tree, pos + NodePos oldPos template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2) From 78a4c611dcde0847e94c7453e0b06dc9d6835011 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sat, 2 Dec 2023 15:26:08 +0300 Subject: [PATCH 10/29] Seperated file --- compiler/nir/gcc_extended_asm.nim | 163 +++++++++----- compiler/nir/genasm.nim | 352 ------------------------------ 2 files changed, 114 insertions(+), 401 deletions(-) delete mode 100644 compiler/nir/genasm.nim diff --git a/compiler/nir/gcc_extended_asm.nim b/compiler/nir/gcc_extended_asm.nim index a424f84fbfa28..53b9ca084026a 100644 --- a/compiler/nir/gcc_extended_asm.nim +++ b/compiler/nir/gcc_extended_asm.nim @@ -1,28 +1,37 @@ -# generates Asm stategments like this: -# Asm { -# AsmTemplate { -# Some asm code -# SymUse nimInlineVar # `a` -# Some asm code -# } -# AsmOutputOperand { -# # [asmSymbolicName] constraint (nimVariableName) -# AsmInjectExpr {symUse nimVariableName} # for output it have only one sym (lvalue) -# asmSymbolicName # default: "" -# constraint -# } -# AsmInputOperand { -# # [asmSymbolicName] constraint (nimExpr) -# AsmInjectExpr {symUse nimVariableName} # (rvalue) -# asmSymbolicName # default: "" -# constraint -# } -# AsmClobber { -# "clobber" -# } - -# it can be useful for better asm analysis and -# easy to use in all nim targets +## GCC Extended asm stategments nodes that produces from NIR. +## It generates iteratively, so parsing doesn't take long + +## Asm stategment structure: +## ```nim +## Asm { +## AsmTemplate { +## Some asm code +## SymUse nimInlineVar # `a` +## Some asm code +## } +## AsmOutputOperand { +## # [asmSymbolicName] constraint (nimVariableName) +## AsmInjectExpr {symUse nimVariableName} # for output it have only one sym (lvalue) +## asmSymbolicName # default: "" +## constraint +## } +## AsmInputOperand { +## # [asmSymbolicName] constraint (nimExpr) +## AsmInjectExpr {symUse nimVariableName} # (rvalue) +## asmSymbolicName # default: "" +## constraint +## } +## AsmClobber { +## "clobber" +## } +## AsmGotoLabel { +## "label" +## } +## ``` + +# It can be useful for better asm analysis and +# easy to use in all nim targets. + import nirinsts, nirtypes import std / assertions import .. / ic / bitabs @@ -34,6 +43,7 @@ type InjectExpr Constraint Clobber + GotoLabel Delimiter AsmValKind = enum @@ -60,24 +70,23 @@ type AsmOutputOperand AsmInputOperand AsmClobber + AsmGotoLabel AsmInjectExpr AsmStrVal GccAsmNode* = ref object - case kind: AsmNodeKind - of AsmTemplate: - instrs: seq[GccAsmNode] + case kind*: AsmNodeKind of AsmOutputOperand, AsmInputOperand: symbolicName: string constraint: string - injectExpr: GccAsmNode - of AsmClobber: - clobber: string - of AsmStrVal: + injectExprs: seq[GccAsmNode] + of AsmStrVal, AsmClobber, AsmGotoLabel: s: string of AsmInjectExpr: n: NodePos + of AsmTemplate: + sons: seq[GccAsmNode] proc toVal(n: NodePos): AsmVal = @@ -102,7 +111,7 @@ proc toNode(val: AsmVal): GccAsmNode = proc emptyNode(kind: AsmNodeKind): GccAsmNode = GccAsmNode(kind: kind) -iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = +iterator asmTokens(t: Tree, n: NodePos; verbatims: BiTable[string]): AsmToken = template addCaptured: untyped = yield ( sec, @@ -119,6 +128,7 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = var det: Det = AsmTemplate var left = 0 var captured = "" + var nPar = 0 # handling comments var @@ -130,7 +140,7 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = for ch in sons(t, n): case t[ch].kind of Verbatim: - let s = ""#it.strVal + let s = verbatims[t[ch].litId] for i in 0..s.high: @@ -158,6 +168,8 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = continue foundCommentEndSym = false if sec > 0 and s[i] == '/': # '/' + if captured != "/": + maybeAddCaptured() foundCommentStartSym = true if sec > 0 and s[i] == '\n' and inComment: if not isLineComment: # /* comment \n @@ -170,7 +182,20 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = # skip commented syms continue + # Inject expr parens + + if s[i] == '(': + inc nPar + elif s[i] == ')': + if nPar > 1: + captured.add ')' + dec nPar + + if nPar > 1: + captured.add s[i] + # no need parsing of expr + continue case s[i]: of ':': @@ -181,9 +206,11 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = det ) + maybeAddCaptured() inc sec # inc det left = i + 1 + captured = "" if sec in 1..2: @@ -191,6 +218,8 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = det = Constraint elif sec == 3: det = Clobber + elif sec == 4: + det = GotoLabel of '[': # start of asm symbolic name @@ -205,7 +234,7 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = det = Constraint # s[capturedStart .. i - 1] - of '(': + of '(': addCaptured() # add asm constraint det = InjectExpr @@ -219,7 +248,7 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = if sec in 1..2: det = Constraint - if sec == 3: + if sec in {3, 4}: maybeAddCaptured() yield ( @@ -227,6 +256,12 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = empty(), Delimiter ) + + # Capture + elif sec == 0 and det == AsmTemplate: + # asm template should not change, + # so we don't skip spaces, etc. + captured.add s[i] elif ( sec > 0 and @@ -234,15 +269,18 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = SymbolicName, Constraint, InjectExpr, - Clobber + Clobber, + GotoLabel } and s[i] notin {' ', '\n', '\t'} - ): captured.add s[i] - - + ): + captured.add s[i] else: discard else: + left = 0 + if captured == "/": + continue maybeAddCaptured() yield ( @@ -250,28 +288,30 @@ iterator asmTokens(t: Tree, n: NodePos; lit: Literals): AsmToken = ch.toVal, det ) - - left = 0 if sec == 0: # : not specified yield ( sec, - lit.strings[t[lastSon(t, n)].litId].toVal, + verbatims[t[lastSon(t, n)].litId].toVal, det ) elif sec > 2: maybeAddCaptured() + + if sec > 4: + raiseAssert"must be maximum 4 asm sections" const sections = [ AsmNodeKind.AsmTemplate, AsmOutputOperand, AsmInputOperand, - AsmClobber + AsmClobber, + AsmGotoLabel ] -iterator parseGccAsm*(t: Tree, n: NodePos; lit: Literals): GccAsmNode = +iterator parseGccAsm*(t: Tree, n: NodePos; verbatims: BiTable[string]): GccAsmNode = var oldSec = 0 curr = emptyNode(AsmTemplate) @@ -280,7 +320,9 @@ iterator parseGccAsm*(t: Tree, n: NodePos; lit: Literals): GccAsmNode = template initNextNode: untyped = curr = emptyNode(sections[i.sec]) - for i in asmTokens(t, n, lit): + for i in asmTokens(t, n, verbatims): + when false: + echo i if i.sec != oldSec: # current node fully filled yield curr @@ -292,7 +334,7 @@ iterator parseGccAsm*(t: Tree, n: NodePos; lit: Literals): GccAsmNode = initNextNode() of AsmTemplate: - curr.instrs.add i.val.toNode + curr.sons.add i.val.toNode of SymbolicName: curr.symbolicName = i.val.s @@ -303,12 +345,35 @@ iterator parseGccAsm*(t: Tree, n: NodePos; lit: Literals): GccAsmNode = curr.constraint = s[1..^2] of InjectExpr: # only one inject expr for now - curr.injectExpr = i.val.toNode + curr.injectExprs.add i.val.toNode of Clobber: let s = i.val.s if s[0] != '"' or s[^1] != '"': raiseAssert "clobber must be started and ended by " & '"' - curr.clobber = s[1..^2] + curr.s = s[1..^2] + + of GotoLabel: + curr.s = i.val.s oldSec = i.sec + + yield curr + +proc `$`*(node: GccAsmNode): string = + case node.kind: + of AsmStrVal, AsmClobber, AsmGotoLabel: node.s + of AsmOutputOperand, AsmInputOperand: + var res = '[' & node.symbolicName & ']' & '"' & node.constraint & '"' & "(`" + for i in node.injectExprs: + res.add $i + res.add "`)" + res + of AsmTemplate: + var res = "" + for i in node.sons: + res.add $i + res.add '\n' + res + of AsmInjectExpr: + "inject node: " & $node.n.int diff --git a/compiler/nir/genasm.nim b/compiler/nir/genasm.nim deleted file mode 100644 index f0592b39dbc8c..0000000000000 --- a/compiler/nir/genasm.nim +++ /dev/null @@ -1,352 +0,0 @@ -# generates Asm stategments like this: -# Asm { -# AsmTemplate { -# Some asm code -# SymUse nimInlineVar # `a` -# Some asm code -# } -# AsmOutputOperand { -# # [asmSymbolicName] constraint (nimVariableName) -# AsmInjectExpr {symUse nimVariableName} # for output it have only one sym (lvalue) -# asmSymbolicName # default: "" -# constraint -# } -# AsmInputOperand { -# # [asmSymbolicName] constraint (nimExpr) -# AsmInjectExpr {symUse nimVariableName} # (rvalue) -# asmSymbolicName # default: "" -# constraint -# } -# AsmClobber { -# "clobber" -# } - -# it can be useful for better asm analysis and -# easy to use in all nim targets - -type - Det = enum - AsmTemplate - SymbolicName - InjectExpr - Constraint - Clobber - Delimiter - - AsmValKind = enum - StrVal - SymVal - NodeVal - EmptyVal - - AsmVal = object - case kind: AsmValKind - of StrVal: - s: string - of SymVal: - sym: SymId - of NodeVal: - n: PNode - of EmptyVal: - discard - - ParsedAsmChunk = tuple[sec: int, val: AsmVal, det: Det] - ParsedAsm = seq[ParsedAsmChunk] - -const - asmSections = [ - Opcode.AsmTemplate, - AsmOutputOperand, - AsmInputOperand, - AsmClobber - ] - -template createSectionItem: untyped = - prepare(c.code, info, asmSections[sec]) - -template closeItem: untyped = - patch(c.code, pos) - -proc toVal(n: PNode): AsmVal = - AsmVal(kind: NodeVal, n: n) - -proc toVal(s: string): AsmVal = - AsmVal(kind: StrVal, s: s) - -proc toVal(s: SymId): AsmVal = - AsmVal(kind: SymVal, sym: s) - -proc empty(): AsmVal = - AsmVal(kind: EmptyVal) - -# proc toVal() - -iterator parseAsm(c: var ProcCon, n: PNode): ParsedAsmChunk = - template addCaptured: untyped = - yield ( - sec, - captured.toVal, - det - ) - captured = "" - - template maybeAddCaptured: untyped = - if captured != "": - addCaptured() - - var sec = 0 - var det: Det = AsmTemplate - var left = 0 - var captured = "" - - # handling comments - var - inComment = false # current char in comment(note: comment chars is skipped) - isLineComment = false - foundCommentStartSym = false - foundCommentEndSym = false - - for it in n.sons: - case it.kind - of nkStrLit..nkTripleStrLit: - let s = it.strVal - - for i in 0..s.high: - - # Comments - if sec > 0 and foundCommentStartSym: - # "/?" - if s[i] == '/': - # "//" - inComment = true - isLineComment = true - elif s[i] == '*': - # "/*" - inComment = true - isLineComment = false - foundCommentStartSym = false # updates it - - if sec > 0 and not foundCommentStartSym and s[i] == '*': - #"(!/)*" - foundCommentEndSym = true - elif sec > 0 and foundCommentEndSym: # "*?" - if s[i] == '/': # "*/" - inComment = false - # delete captured '/' - captured = "" - continue - foundCommentEndSym = false - if sec > 0 and s[i] == '/': # '/' - foundCommentStartSym = true - if sec > 0 and s[i] == '\n' and inComment: - if not isLineComment: # /* comment \n - raiseAssert """expected "*/", not "*""" & s[i] & """" in asm operand""" - inComment = false - # delete captured '/' - captured = "" - continue - if inComment: - # skip commented syms - continue - - - - case s[i]: - of ':': - if sec == 0: # det == AsmTemplate - yield ( - sec, - s[left..i - 1].toVal, - det - ) - - inc sec - # inc det - left = i + 1 - captured = "" - - if sec in 1..2: - # default det for operands - det = Constraint - elif sec == 3: - det = Clobber - - of '[': - # start of asm symbolic name - det = SymbolicName - - of ']': - if det != SymbolicName: - raiseAssert "expected: ']'" - - addCaptured() - - det = Constraint - # s[capturedStart .. i - 1] - - of '(': - addCaptured() # add asm constraint - det = InjectExpr - - of ')': - if det != InjectExpr: - raiseAssert "expected: ')'" - - maybeAddCaptured() - - elif sec > 0 and s[i] == ',': - if sec in 1..2: - det = Constraint - - if sec == 3: - maybeAddCaptured() - - yield ( - sec, - empty(), - Delimiter - ) - - elif ( - sec > 0 and - det in { - SymbolicName, - Constraint, - InjectExpr, - Clobber - } and - s[i] notin {' ', '\n', '\t'} - ): captured.add s[i] - - - else: discard - - else: - maybeAddCaptured() - - yield ( - sec, - if it.kind == nkSym: - toSymId(c, it.sym).toVal - else: - it.toVal, - det - ) - - left = 0 - - if sec == 0: - # : not specified - yield ( - sec, - n[^1].strVal.toVal, - det - ) - elif sec > 2: - maybeAddCaptured() - -proc genInlineAsm(c: var ProcCon; n: PNode) = - template createInstr(sec: int): untyped = - prepare(c.code, info, asmSections[sec]) - - template createInstr(instr: Opcode): untyped = - prepare(c.code, info, instr) - - template endInstr: untyped = - patch(c.code, pos) - - template maybeEndOperand(): untyped = - if inInjectExpr: - # end of old operand - endInstr() # AsmInjectExpr node - c.code.addStrVal c.lit.strings, info, asmSymbolicName - c.code.addStrVal c.lit.strings, info, constraint - - pos = oldPos - - #default operand info - inInjectExpr = false - asmSymbolicName = "" - constraint = "" - - let info = toLineInfo(c, n.info) - build c.code, info, Asm: - var - pos = createInstr(AsmTemplate) - oldPos = pos - - oldSec = 0 - # operands - asmSymbolicName = "" - constraint = "" - inInjectExpr = false - - for i in parseAsm(c, n): - when false: - echo i - - if i.sec != oldSec: - # new sec - maybeEndOperand() - - endInstr() - pos = createInstr(i.sec) - - case i.det: - of AsmTemplate: - # it's tmp code, template can contains nodes - c.code.addStrVal c.lit.strings, info, i.val.s - - of SymbolicName: - asmSymbolicName = i.val.s - of Constraint: - let s = i.val.s - if s[0] != '"' or s[^1] != '"': - raiseAssert "constraint must be started or ended by " & '"' - constraint = s[1..^2] - of InjectExpr: - # (`dst`) - if not inInjectExpr: - oldPos = pos - pos = createInstr(AsmInjectExpr) - - case i.val.kind: - of SymVal: - c.code.addSymUse info, i.val.sym - of StrVal: - c.code.addStrVal c.lit.strings, info, i.val.s - of NodeVal: - raiseAssert "unsupported" - of EmptyVal: raiseAssert "never" - - inInjectExpr = true - - of Delimiter: - maybeEndOperand() - - endInstr() - pos = createInstr(i.sec) - - of Clobber: - let s = i.val.s - if s[0] != '"' or s[^1] != '"': - raiseAssert "clobber must be started or ended by " & '"' - c.code.addStrVal c.lit.strings, info, s[1..^2] - - oldSec = i.sec - - maybeEndOperand() - - endInstr() - -proc genGlobalAsm(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - build c.code, info, AsmGlobal: - for i in n: - case i.kind - of nkStrLit..nkTripleStrLit: - c.code.addStrVal c.lit.strings, info, i.strVal - of nkSym: - c.code.addSymUse info, toSymId(c, i.sym) - else: - gen(c, i) From e4af3f0c14bf9dc796287d34c07c79dd937dfc30 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sat, 2 Dec 2023 15:57:33 +0300 Subject: [PATCH 11/29] target properties --- compiler/nir/cir.nim | 18 +++++++++++++----- compiler/nir/nirc.nim | 19 ++++++++++++++++++- compiler/nir/target_props.nim | 8 ++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 compiler/nir/target_props.nim diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 257bfae72b9d5..83567018467d9 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -14,6 +14,7 @@ from std / strutils import toOctal import .. / ic / [bitabs, rodfiles] import nirtypes, nirinsts, nirfiles import ../../dist/checksums/src/checksums/md5 +import target_props type Token = LitId # indexing into the tokens BiTable[string] @@ -64,6 +65,7 @@ proc fillTokenTable(tab: var BiTable[string]) = type GeneratedCode* = object m: NirModule + props: TargetProps includes: seq[LitId] includedHeaders: IntSet data: seq[LitId] @@ -76,8 +78,11 @@ type generatedTypes: IntSet mangledModules: Table[LitId, LitId] -proc initGeneratedCode*(m: sink NirModule): GeneratedCode = - result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]()) +proc initGeneratedCode*(m: sink NirModule; props: sink TargetProps): GeneratedCode = + result = GeneratedCode( + m: m, code: @[], tokens: initBiTable[string](), + props: props + ) fillTokenTable(result.tokens) proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} = @@ -850,7 +855,10 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case target: of Asm: - genGccAsm(c, t, code) + case c.props.inlineAsmSyntax: + of None: discard + of GCCExtendedAsm: genGccAsm(c, t, code) + of VisualCPP: raiseAssert"not implemented" of Code: raiseAssert"not supported" @@ -1002,8 +1010,8 @@ proc traverseCode(c: var GeneratedCode) = c.init.add c.code[i] setLen c.code, oldLen -proc generateCode*(inp, outp: string) = - var c = initGeneratedCode(load(inp)) +proc generateCode*(inp, outp: string; props: sink TargetProps) = + var c = initGeneratedCode(load(inp), props) var co = TypeOrder() traverseTypes(c.m.types, c.m.lit, co) diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim index fb1bd7cf8892e..4fd98897eee7e 100644 --- a/compiler/nir/nirc.nim +++ b/compiler/nir/nirc.nim @@ -11,6 +11,7 @@ import ".." / ic / [bitabs, rodfiles] import nirinsts, nirtypes, nirlineinfos, nirfiles, cir +import target_props proc view(filename: string) = let m = load(filename) @@ -29,6 +30,8 @@ proc writeHelp = proc main = var inp = "" var cmd = "" + var props = TargetProps() + for kind, key, val in getopt(): case kind of cmdArgument: @@ -39,6 +42,20 @@ proc main = case key of "help", "h": writeHelp() of "version", "v": stdout.write "1.0\n" + of "inlineAsmSyntax", "a": + if val == "": + quit "Error: no inline asm syntax specified" + + case val: + of "none": + props.inlineAsmSyntax = None + of "gcc-like": + props.inlineAsmSyntax = GCCExtendedAsm + of "msvc-like": + props.inlineAsmSyntax = VisualCPP + else: + quit "Error: invalid inline asm syntax. Must be: gcc-like or msvc-like (or none)" + of cmdEnd: discard if inp.len == 0: quit "Error: no input file specified" @@ -47,6 +64,6 @@ proc main = view inp of "c": let outp = inp & ".c" - cir.generateCode inp, outp + cir.generateCode inp, outp, props main() diff --git a/compiler/nir/target_props.nim b/compiler/nir/target_props.nim new file mode 100644 index 0000000000000..7d30ce221252f --- /dev/null +++ b/compiler/nir/target_props.nim @@ -0,0 +1,8 @@ +type + InlineAsmSyntax* = enum + None + GCCExtendedAsm + VisualCPP + + TargetProps* = object + inlineAsmSyntax*: InlineAsmSyntax From e9221aed58ddfcba6e0e504c2bd95d119e9626e2 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 01:49:47 +0300 Subject: [PATCH 12/29] GCC asm c codegen It don't use strutils :) --- compiler/nir/cir.nim | 61 +++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 83567018467d9..60048cff5101f 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -565,28 +565,49 @@ proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = c.add ParLe c.add NewLine - var sec = 0 - template maybeAddQuotes: untyped = + var + sec = 0 + left = 0 + s = "" + + template maybeAddQuote: untyped = if sec == 0: c.add """"""" - template maybeAddQuoted(s: string): untyped = - maybeAddQuotes - c.add s - maybeAddQuotes - - for i in sons(t, n): - if t[i].kind == Verbatim: - let s = c.m.lit.verbatims[t[i].litId] - var left = 0 - for j in 0..s.high: - if s[j] == '\n': - maybeAddQuoted s[left..j-1] & (if sec == 0: r"\n" else: "") + + template beforeSameSection(s: string): bool = + # just see that after spaces : + # It's O(1) with high probability + var beforeNext = false + var notFinal = false + for i in s: + if i == ':': beforeNext = true + if i notin {' ', '\t', '\n'}: + notFinal = true + break + not beforeNext and notFinal + + var newLine = false + maybeAddQuote # first " + for ch in sons(t, n): + if t[ch].kind == Verbatim: + s = c.m.lit.verbatims[t[ch].litId] + left = 0 + for i in 0..s.high: + if s[i] == '\n': + newLine = true + c.add s[left..i-1] + maybeAddQuote + left = i + 1 c.add NewLine - left = j + 1 - elif s[j] == ':': inc sec - - maybeAddQuoted s[left..^1] - else: c.gen(t, i) - c.add NewLine + if beforeSameSection(s[i+1..^1]): maybeAddQuote + elif s[i] == ':': inc sec + else: + c.add s[left..^1] + c.gen(t, ch) + if not newLine: + c.add s[left..^1] + maybeAddQuote + c.add NewLine + c.add ParRi c.add Semicolon From 84191517b91addc18647e743f0615ba3f1d128d9 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 01:56:07 +0300 Subject: [PATCH 13/29] Small fix --- compiler/nir/cir.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 60048cff5101f..096280312b7d3 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -595,6 +595,7 @@ proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = if s[i] == '\n': newLine = true c.add s[left..i-1] + if sec == 0: c.add r"\n" maybeAddQuote left = i + 1 c.add NewLine From 57802edaf7c0b3bfede766124dd121bc5e786729 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 16:34:19 +0300 Subject: [PATCH 14/29] apply suggestions --- compiler/nir/nirinsts.nim | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 4036b20067caa..f91cb87ebf7fa 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -253,18 +253,7 @@ proc nextChild(tree: Tree; pos: var int) {.inline.} = proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos) template firstSon*(n: NodePos): NodePos = NodePos(n.int+1) -proc lastSon*(tree: Tree; n: NodePos): NodePos = - var - pos = n.int - oldPos = pos - assert tree.nodes[pos].kind > LastAtomicValue - let last = pos + tree.nodes[pos].rawSpan - inc pos - while pos < last: - oldPos = pos - nextChild tree, pos - NodePos oldPos template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2) @@ -311,6 +300,28 @@ iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos = proc span(tree: Tree; pos: int): int {.inline.} = if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) +proc parentImpl(tree: Tree; n: NodePos): NodePos = + # finding the parent of a node is rather easy: + var pos = n.int - 1 + while pos >= 0 and isAtom(tree, pos) or (pos + tree.nodes[pos].rawSpan - 1 < n.int): + dec pos + assert pos >= 0, "node has no parent" + result = NodePos(pos) + +proc isLastSon*(tree: Tree; parent, n: NodePos): bool {.inline.} = + # A node is a the last son of a parent node if its span + # falls onto the end of the parent's span: + let last = n.int + span(tree, n.int) + result = last == parent.int + span(tree, parent.int) + +proc lastSon*(tree: Tree; n: NodePos): NodePos = + assert(not isAtom(tree, n.int)) + result = NodePos(n.int + span(tree, n.int) - 1) + while true: + let p = parentImpl(tree, result) + if p.int == n.int: break + result = p + proc copyTree*(dest: var Tree; src: Tree) = let pos = 0 let L = span(src, pos) From a1f9c3fff2134be4e8fa5355285e8d6a3aa12b2d Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 17:49:00 +0300 Subject: [PATCH 15/29] msvc asm --- compiler/nir/cir.nim | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 096280312b7d3..966eb6b935a7b 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -49,7 +49,6 @@ type CaseKeyword = "case " DefaultKeyword = "default:" BreakKeyword = "break" - AsmKeyword = "__asm__ " NullPtr = "nullptr" IfNot = "if (!(" ReturnKeyword = "return " @@ -561,7 +560,7 @@ template moveToDataSection(body: untyped) = setLen c.code, oldLen proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = - c.add AsmKeyword + c.add "__asm__ " c.add ParLe c.add NewLine @@ -580,7 +579,7 @@ proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = var notFinal = false for i in s: if i == ':': beforeNext = true - if i notin {' ', '\t', '\n'}: + if i notin {' ', '\t'}: notFinal = true break not beforeNext and notFinal @@ -612,6 +611,12 @@ proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = c.add ParRi c.add Semicolon +proc genVisualCPPAsm(c: var GeneratedCode; t: Tree; n: NodePos) = + c.add "__asm " + c.add CurlyLe + c.gen(t, n) # inline asm + c.add CurlyRi + proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case t[n].kind of Nop: @@ -877,16 +882,24 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case target: of Asm: + let isInlineAsm = + # not fetchInfo(IsGlobal).infoVal(bool) or + c.props.inlineAsmSyntax == VisualCPP + case c.props.inlineAsmSyntax: - of None: discard + of None: raiseAssert "Your compiler does not support the inline assembler" of GCCExtendedAsm: genGccAsm(c, t, code) - of VisualCPP: raiseAssert"not implemented" + of VisualCPP: genVisualCPPAsm(c, t, code) of Code: raiseAssert"not supported" - of EmitTarget, EmitCode: + of EmitTarget: discard + of EmitCode: + for ch in sons(t, n): + gen c, t, ch + const Prelude = """ /* GENERATED CODE. DO NOT EDIT. */ From f41557d61bfc2ffbdd20be71184494275e95cf1a Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 20:43:44 +0300 Subject: [PATCH 16/29] info's --- compiler/nir/ast2ir.nim | 1 + compiler/nir/cir.nim | 44 ++++++++++++++++++++++++++++---------- compiler/nir/nirinsts.nim | 45 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 8d989dc345c82..2935833f80329 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2532,6 +2532,7 @@ proc genAsm(c: var ProcCon; n: PNode) = let info = toLineInfo(c, n.info) build c.code, info, Emit: c.code.addEmitTarget info, Asm + c.code.addBoolInfo info, IsGlobal, true#c.prc == nil genEmitCode c, n diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 966eb6b935a7b..bac67747e1b75 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -617,6 +617,20 @@ proc genVisualCPPAsm(c: var GeneratedCode; t: Tree; n: NodePos) = c.gen(t, n) # inline asm c.add CurlyRi +proc genBasicAsm(c: var GeneratedCode; t: Tree; n: NodePos) = + assert ( + t[n].rawOperand - 1 == t[n.firstSon].rawOperand and + t[n.firstSon].kind == Verbatim + ), "Invalid basic asm. Basic asm should be only one verbatim" + + let s = c.m.lit.verbatims[t[n.firstSon].litId] + var left = 0 + for j in 0..s.high: + if s[j] == '\n': + c.add makeCString(s[left..j]) + c.add NewLine + left = j + 1 + proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case t[n].kind of Nop: @@ -877,19 +891,24 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of Verbatim: c.add c.m.lit.verbatims[t[n].litId] of Emit: - let (targetRaw, code) = sons2(t, n) - let target = cast[EmitTargetKind](t[targetRaw].rawOperand) - + requireInfo t, n, IsGlobal + let + target = cast[EmitTargetKind](t[n.firstSon].rawOperand) + code = lastSon(t, n) + case target: of Asm: - let isInlineAsm = - # not fetchInfo(IsGlobal).infoVal(bool) or - c.props.inlineAsmSyntax == VisualCPP - - case c.props.inlineAsmSyntax: - of None: raiseAssert "Your compiler does not support the inline assembler" - of GCCExtendedAsm: genGccAsm(c, t, code) - of VisualCPP: genVisualCPPAsm(c, t, code) + let isBasicAsm = + fetchInfo(t, n, IsGlobal).infoVal(t, bool) and + c.props.inlineAsmSyntax != VisualCPP + + if not isBasicAsm: + case c.props.inlineAsmSyntax: + of None: raiseAssert "Your compiler does not support the inline assembler" + of GCCExtendedAsm: genGccAsm(c, t, code) + of VisualCPP: genVisualCPPAsm(c, t, code) + else: genBasicAsm(c, t, code) + of Code: raiseAssert"not supported" @@ -900,6 +919,9 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = for ch in sons(t, n): gen c, t, ch + of Info, InfoId: + discard "Info can't generate code" + const Prelude = """ /* GENERATED CODE. DO NOT EDIT. */ diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index f91cb87ebf7fa..02d5e7e07dabc 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -42,6 +42,7 @@ type LoopLabel, GotoLoop, EmitTarget, + InfoId, Verbatim, # last atom ModuleSymUse, # `"module".x` @@ -111,6 +112,7 @@ type TestOf, Emit, EmitCode, + Info, ProcDecl, ForeignProcDecl, @@ -126,6 +128,9 @@ type DllExport, ObjExport + InfoKey* = enum + IsGlobal + EmitTargetKind* = enum Asm Code @@ -378,6 +383,36 @@ proc litId*(ins: Instr): LitId {.inline.} = result = LitId(ins.operand) +iterator fetchInfos*(t: Tree; n: NodePos; k: InfoKey): NodePos = + for i in sons(t, n): + if t[i].kind == Info and k == cast[InfoKey](t[i.firstSon].operand): + yield i + +proc fetchInfo*(t: Tree; n: NodePos; k: InfoKey): NodePos = + result = NodePos -1 + for i in fetchInfos(t, n, k): + return i + +proc haveInfo*(t: Tree; n: NodePos; k: InfoKey): bool = + result = false + for i in fetchInfos(t, n, k): + return true + +proc requireInfo*(t: Tree; n: NodePos; k: InfoKey) = + # raises an a error if info not found + if not haveInfo(t, n, k): raiseAssert $k & " info is required" + +const + boolInfos = {IsGlobal} + +proc infoVal*(info: NodePos, tree: Tree, t: type bool): bool = + assert tree[info].kind == Info + let (key, b) = sons2(tree, info) + assert cast[InfoKey](tree[key].operand) in boolInfos, "Info must be in bool infos" + + cast[bool](tree[b].operand) + + type LabelId* = distinct int @@ -462,6 +497,14 @@ proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) = buildTyped t, info, NumberConv, typ: t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info) +proc addInfoId*(t: var Tree; info: PackedLineInfo; x: InfoKey) = + t.nodes.add Instr(x: toX(InfoId, uint32(x)), info: info) + +proc addBoolInfo*(t: var Tree; info: PackedLineInfo; k: InfoKey, b: bool) = + build t, info, Info: + t.addInfoId info, k + t.addImmediateVal info, b.int + proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes @@ -538,6 +581,8 @@ proc toString*(t: Tree; pos: NodePos; strings, verbatims: BiTable[string], integ r.add $cast[EmitTargetKind](t[pos].operand) of PragmaId: r.add $cast[PragmaKey](t[pos].operand) + of InfoId: + r.add $cast[InfoKey](t[pos].operand) of Typed: r.add "T<" r.add $t[pos].operand From fc8b91f23831244e7d71a6006ff55e17ac2e754a Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 20:44:50 +0300 Subject: [PATCH 17/29] fix --- compiler/nir/nirvm.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim index 140b962db81d6..c06883c65049a 100644 --- a/compiler/nir/nirvm.nim +++ b/compiler/nir/nirvm.nim @@ -702,6 +702,8 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla recurse TestOfM of Emit, EmitTarget, EmitCode: raiseAssert "cannot interpret: Emit" + of Info, InfoId: + discard of Verbatim: raiseAssert "cannot interpret: Verbatim" of ProcDecl: From 68bc65db0a2592ef7549d8ce820871fc8bbcd4cf Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 20:53:19 +0300 Subject: [PATCH 18/29] fix --- compiler/nir/ast2ir.nim | 2 +- compiler/nir/cir.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 2935833f80329..3d0ebf9b679c9 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2532,7 +2532,7 @@ proc genAsm(c: var ProcCon; n: PNode) = let info = toLineInfo(c, n.info) build c.code, info, Emit: c.code.addEmitTarget info, Asm - c.code.addBoolInfo info, IsGlobal, true#c.prc == nil + c.code.addBoolInfo info, IsGlobal, c.prc == nil genEmitCode c, n diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index bac67747e1b75..47740ea846681 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -623,7 +623,7 @@ proc genBasicAsm(c: var GeneratedCode; t: Tree; n: NodePos) = t[n.firstSon].kind == Verbatim ), "Invalid basic asm. Basic asm should be only one verbatim" - let s = c.m.lit.verbatims[t[n.firstSon].litId] + let s = c.m.lit.verbatims[t[n.firstSon].litId] & '\n' var left = 0 for j in 0..s.high: if s[j] == '\n': From 3d4f45eea26cf3e446917067f8723da7d7ba9d81 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Sun, 3 Dec 2023 21:15:24 +0300 Subject: [PATCH 19/29] Extended asm is now default inline asm syntax --- compiler/nir/nirc.nim | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim index 4fd98897eee7e..8d68b0cd2b599 100644 --- a/compiler/nir/nirc.nim +++ b/compiler/nir/nirc.nim @@ -30,7 +30,7 @@ proc writeHelp = proc main = var inp = "" var cmd = "" - var props = TargetProps() + var props = TargetProps(inlineAsmSyntax: GCCExtendedAsm) for kind, key, val in getopt(): case kind @@ -46,15 +46,13 @@ proc main = if val == "": quit "Error: no inline asm syntax specified" - case val: - of "none": - props.inlineAsmSyntax = None - of "gcc-like": - props.inlineAsmSyntax = GCCExtendedAsm - of "msvc-like": - props.inlineAsmSyntax = VisualCPP - else: - quit "Error: invalid inline asm syntax. Must be: gcc-like or msvc-like (or none)" + props.inlineAsmSyntax = + case val: + of "none": None + of "gcc-like": GCCExtendedAsm + of "msvc-like": VisualCPP + else: + quit "Error: invalid inline asm syntax. Must be: gcc-like or msvc-like (or none)" of cmdEnd: discard if inp.len == 0: From 24b6c554915050e666959d2eaae7cdef1bda74ac Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Mon, 4 Dec 2023 00:11:59 +0300 Subject: [PATCH 20/29] support asmNoStackFrame --- compiler/nir/ast2ir.nim | 9 ++++++++- compiler/nir/cir.nim | 9 ++++++--- compiler/nir/nirinsts.nim | 6 +++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 3d0ebf9b679c9..694015f397cd8 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2463,7 +2463,7 @@ proc genProc(cOuter: var ProcCon; prc: PSym) = c.code.addPragmaId info, ExternName c.code.addStrVal c.lit.strings, info, prc.loc.r if sfImportc in prc.flags: - if lfHeader in prc. loc.flags: + if lfHeader in prc.loc.flags: assert(prc. annex != nil) let str = getStr(prc. annex.path) build c.code, info, PragmaPair: @@ -2532,7 +2532,14 @@ proc genAsm(c: var ProcCon; n: PNode) = let info = toLineInfo(c, n.info) build c.code, info, Emit: c.code.addEmitTarget info, Asm + c.code.addBoolInfo info, IsGlobal, c.prc == nil + c.code.addBoolInfo info, InPure, ( + if c.prc != nil: + sfPure in c.prc.flags + else: false + ) + genEmitCode c, n diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 47740ea846681..3e6c08af761ad 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -898,9 +898,12 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case target: of Asm: - let isBasicAsm = - fetchInfo(t, n, IsGlobal).infoVal(t, bool) and - c.props.inlineAsmSyntax != VisualCPP + requireInfo t, n, InPure + + let isBasicAsm = ( + fetchInfo(t, n, IsGlobal).infoVal(t, bool) or + fetchInfo(t, n, InPure).infoVal(t, bool) + ) and c.props.inlineAsmSyntax != VisualCPP if not isBasicAsm: case c.props.inlineAsmSyntax: diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 02d5e7e07dabc..ab7affe6d50c0 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -130,6 +130,7 @@ type InfoKey* = enum IsGlobal + InPure EmitTargetKind* = enum Asm @@ -402,8 +403,11 @@ proc requireInfo*(t: Tree; n: NodePos; k: InfoKey) = # raises an a error if info not found if not haveInfo(t, n, k): raiseAssert $k & " info is required" +proc requireInfo*(t: Tree; n: NodePos; k: set[InfoKey]) = + for i in k: requireInfo(t, n, i) + const - boolInfos = {IsGlobal} + boolInfos = {IsGlobal, InPure} proc infoVal*(info: NodePos, tree: Tree, t: type bool): bool = assert tree[info].kind == Info From 5f58ec9fd3943f74d273330cdcdc814339a82197 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Mon, 4 Dec 2023 14:24:43 +0300 Subject: [PATCH 21/29] use strings for verbatims --- compiler/ic/rodfiles.nim | 1 - compiler/nir/ast2ir.nim | 2 +- compiler/nir/cir.nim | 6 +++--- compiler/nir/nirc.nim | 2 +- compiler/nir/nirfiles.nim | 6 ------ compiler/nir/nirinsts.nim | 10 +++++----- compiler/nir/nirtypes.nim | 1 - compiler/nir/nirvm.nim | 2 +- 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 992944c2df974..5eef3874a2995 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -73,7 +73,6 @@ type versionSection configSection stringsSection - verbatimsSection checkSumsSection depsSection numbersSection diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 694015f397cd8..03030c6a752e5 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2522,7 +2522,7 @@ template genEmitCode(c: var ProcCon; n: PNode) = for i in n: case i.kind: of nkStrLit..nkTripleStrLit: - c.code.addVerbatim c.lit.verbatims, info, i.strVal + c.code.addVerbatim c.lit.strings, info, i.strVal of nkSym: c.code.addSymUse info, toSymId(c, i.sym) else: diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 3e6c08af761ad..42388d3aff780 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -588,7 +588,7 @@ proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = maybeAddQuote # first " for ch in sons(t, n): if t[ch].kind == Verbatim: - s = c.m.lit.verbatims[t[ch].litId] + s = c.m.lit.strings[t[ch].litId] left = 0 for i in 0..s.high: if s[i] == '\n': @@ -623,7 +623,7 @@ proc genBasicAsm(c: var GeneratedCode; t: Tree; n: NodePos) = t[n.firstSon].kind == Verbatim ), "Invalid basic asm. Basic asm should be only one verbatim" - let s = c.m.lit.verbatims[t[n.firstSon].litId] & '\n' + let s = c.m.lit.strings[t[n.firstSon].litId] & '\n' var left = 0 for j in 0..s.high: if s[j] == '\n': @@ -889,7 +889,7 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc: c.add "cannot interpret: " & $t[n].kind of Verbatim: - c.add c.m.lit.verbatims[t[n].litId] + c.add c.m.lit.strings[t[n].litId] of Emit: requireInfo t, n, IsGlobal let diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim index 8d68b0cd2b599..ccf8c2f3ad64b 100644 --- a/compiler/nir/nirc.nim +++ b/compiler/nir/nirc.nim @@ -16,7 +16,7 @@ import target_props proc view(filename: string) = let m = load(filename) var res = "" - allTreesToString m.code, m.lit.strings, m.lit.verbatims, m.lit.numbers, m.symnames, res + allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res res.add "\n# TYPES\n" nirtypes.toString res, m.types echo res diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim index b7dcba82fcf16..cd5a79f06e444 100644 --- a/compiler/nir/nirfiles.nim +++ b/compiler/nir/nirfiles.nim @@ -29,9 +29,6 @@ proc load*(filename: string): NirModule = r.loadSection stringsSection r.load result.lit.strings - r.loadSection verbatimsSection - r.load result.lit.verbatims - r.loadSection numbersSection r.load result.lit.numbers @@ -61,9 +58,6 @@ proc store*(m: NirModule; outp: string) = r.storeSection stringsSection r.store m.lit.strings - r.storeSection verbatimsSection - r.store m.lit.verbatims - r.storeSection numbersSection r.store m.lit.numbers diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index ab7affe6d50c0..de150842340d7 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -554,7 +554,7 @@ template localName(s: SymId): string = proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s) proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s) -proc toString*(t: Tree; pos: NodePos; strings, verbatims: BiTable[string], integers: BiTable[int64]; +proc toString*(t: Tree; pos: NodePos; strings: BiTable[string], integers: BiTable[int64]; names: SymNames; r: var string; nesting = 0) = const tripleQuote = """"""""" @@ -572,7 +572,7 @@ proc toString*(t: Tree; pos: NodePos; strings, verbatims: BiTable[string], integ escapeToNimLit(strings[LitId t[pos].operand], r) of Verbatim: r.add "Verbatim " & tripleQuote & '\n' - r.add verbatims[LitId t[pos].operand] + r.add strings[LitId t[pos].operand] r.add '\n' & tripleQuote of SymDef: r.add "SymDef " @@ -609,17 +609,17 @@ proc toString*(t: Tree; pos: NodePos; strings, verbatims: BiTable[string], integ r.add "{\n" for i in 0..<(nesting+1)*2: r.add ' ' for p in sons(t, pos): - toString t, p, strings, verbatims, integers, names, r, nesting+1 + toString t, p, strings, integers, names, r, nesting+1 r.add "\n" for i in 0.. Date: Mon, 4 Dec 2023 15:10:51 +0300 Subject: [PATCH 22/29] fix asmNoStackFrame --- compiler/nir/ast2ir.nim | 1 + compiler/nir/cir.nim | 11 +++++++++-- compiler/nir/nirinsts.nim | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 03030c6a752e5..7006aefdae44b 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -2454,6 +2454,7 @@ proc genProc(cOuter: var ProcCon; prc: PSym) = c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(prc)) c.code.addImmediateVal info, prc.itemId.item.int addCallConv c, info, prc.typ.callConv + if sfPure in prc.flags: c.code.addPragmaId info, AsmNoStackFrame if sfCompilerProc in prc.flags: build c.code, info, PragmaPair: c.code.addPragmaId info, CoreName diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 42388d3aff780..b916213ca7965 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -449,7 +449,9 @@ proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) = let lit = t[y].litId raiseAssert "cannot eval: " & c.m.lit.strings[lit] else: discard - of PragmaId: discard + of PragmaId: + let key = cast[PragmaKey](t[prc].rawOperand) + if key == AsmNoStackFrame: c.add "NIM_NAKED " else: break next t, prc @@ -619,7 +621,7 @@ proc genVisualCPPAsm(c: var GeneratedCode; t: Tree; n: NodePos) = proc genBasicAsm(c: var GeneratedCode; t: Tree; n: NodePos) = assert ( - t[n].rawOperand - 1 == t[n.firstSon].rawOperand and + isLastSon(t, n, n.firstSon) and t[n.firstSon].kind == Verbatim ), "Invalid basic asm. Basic asm should be only one verbatim" @@ -1052,6 +1054,11 @@ typedef NU8 NU; #define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x}) #define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x}) +#ifdef _MSC_VER +#define NIM_NAKED __declspec(naked) +#else +#define NIM_NAKED __attribute__((naked)) +#endif """ proc traverseCode(c: var GeneratedCode) = diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index de150842340d7..914a3a1cec3a7 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -126,7 +126,8 @@ type HeaderImport, DllImport, DllExport, - ObjExport + ObjExport, + AsmNoStackFrame InfoKey* = enum IsGlobal From 21ee838ebe37671867e642a4367f54c6f0441510 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Mon, 4 Dec 2023 16:17:41 +0300 Subject: [PATCH 23/29] fix --- compiler/nir/cir.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index b916213ca7965..62eeec7eb9fe1 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -627,11 +627,16 @@ proc genBasicAsm(c: var GeneratedCode; t: Tree; n: NodePos) = let s = c.m.lit.strings[t[n.firstSon].litId] & '\n' var left = 0 + c.add "__asm__ " + c.add ParLe + c.add NewLine for j in 0..s.high: if s[j] == '\n': c.add makeCString(s[left..j]) c.add NewLine left = j + 1 + c.add ParRi + c.add Semicolon proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case t[n].kind From cc45710d3960847df60dd8183d7d7a6105a50598 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Thu, 7 Dec 2023 19:57:12 +0300 Subject: [PATCH 24/29] small refactor --- compiler/nir/cir.nim | 77 ++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 62eeec7eb9fe1..c26c8dd284341 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -32,6 +32,7 @@ type Semicolon = ";" Comma = ", " Space = " " + Quote = $'"' Colon = ": " Dot = "." Arrow = "->" @@ -566,50 +567,40 @@ proc genGccAsm(c: var GeneratedCode; t: Tree; n: NodePos) = c.add ParLe c.add NewLine - var - sec = 0 - left = 0 - s = "" - - template maybeAddQuote: untyped = - if sec == 0: c.add """"""" - - template beforeSameSection(s: string): bool = - # just see that after spaces : - # It's O(1) with high probability - var beforeNext = false - var notFinal = false + var asmTemplate = true + var left = 0 + var s = "" + + template maybeAddQuote = + if asmTemplate: c.add Quote + + template findSecUpdate(s: string): bool = + var result = false for i in s: - if i == ':': beforeNext = true - if i notin {' ', '\t'}: - notFinal = true - break - not beforeNext and notFinal - - var newLine = false - maybeAddQuote # first " + if i == ':': result = true + if i notin {' ', '\t', '\n'}: break #found char + result + + maybeAddQuote for ch in sons(t, n): if t[ch].kind == Verbatim: s = c.m.lit.strings[t[ch].litId] left = 0 for i in 0..s.high: if s[i] == '\n': - newLine = true - c.add s[left..i-1] - if sec == 0: c.add r"\n" + c.add s[left..i - 1] + if asmTemplate: c.add r"\n" maybeAddQuote left = i + 1 c.add NewLine - if beforeSameSection(s[i+1..^1]): maybeAddQuote - elif s[i] == ':': inc sec + if not findSecUpdate(s[i+1..^1]): maybeAddQuote # next '"' + elif s[i] == ':': asmTemplate = false else: c.add s[left..^1] c.gen(t, ch) - if not newLine: + if not findSecUpdate(s): c.add s[left..^1] maybeAddQuote - c.add NewLine - c.add ParRi c.add Semicolon @@ -619,25 +610,6 @@ proc genVisualCPPAsm(c: var GeneratedCode; t: Tree; n: NodePos) = c.gen(t, n) # inline asm c.add CurlyRi -proc genBasicAsm(c: var GeneratedCode; t: Tree; n: NodePos) = - assert ( - isLastSon(t, n, n.firstSon) and - t[n.firstSon].kind == Verbatim - ), "Invalid basic asm. Basic asm should be only one verbatim" - - let s = c.m.lit.strings[t[n.firstSon].litId] & '\n' - var left = 0 - c.add "__asm__ " - c.add ParLe - c.add NewLine - for j in 0..s.high: - if s[j] == '\n': - c.add makeCString(s[left..j]) - c.add NewLine - left = j + 1 - c.add ParRi - c.add Semicolon - proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case t[n].kind of Nop: @@ -917,8 +889,13 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = of None: raiseAssert "Your compiler does not support the inline assembler" of GCCExtendedAsm: genGccAsm(c, t, code) of VisualCPP: genVisualCPPAsm(c, t, code) - else: genBasicAsm(c, t, code) - + else: + assert ( + isLastSon(t, code, code.firstSon) and + t[code.firstSon].kind == Verbatim + ), "Invalid basic asm. Basic asm should be only one verbatim" + genGccAsm(c, t, code) + of Code: raiseAssert"not supported" From 11a6fe856b8991f5c893e389e7ae4520abf4875c Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Tue, 12 Dec 2023 13:13:45 +0300 Subject: [PATCH 25/29] inlineAsmSyntax pragma --- compiler/nir/ast2ir.nim | 41 ++++++++++++++++++++++++++--------- compiler/nir/cir.nim | 23 +++++++++++++++----- compiler/nir/nirc.nim | 13 +++++------ compiler/nir/nirinsts.nim | 14 +++++++++++- compiler/nir/target_props.nim | 5 ++--- compiler/pragmas.nim | 32 +++++++++++++++------------ compiler/wordrecg.nim | 2 +- 7 files changed, 89 insertions(+), 41 deletions(-) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 7006aefdae44b..95e618d80f346 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -10,7 +10,7 @@ import std / [assertions, tables, sets] import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys, modulegraphs, renderer, transf, bitsets, trees, nimsets, - expanddefaults] + expanddefaults, wordrecg] from ".." / lowerings import lowerSwap, lowerTupleUnpacking from ".." / pathutils import customPath import .. / ic / bitabs @@ -2518,19 +2518,32 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = else: genCall c, n, d -template genEmitCode(c: var ProcCon; n: PNode) = +template genEmitCode(c: var ProcCon; n: PNode; isAsmStmt: bool) = + let offset = + if isAsmStmt: 1 # first son is pragmas + else: 0 + build c.code, info, EmitCode: - for i in n: - case i.kind: + for i in offset.. 1: + raiseAssert "Ambiguous asm syntax, please specify via inlineAsmSyntax pragma" + elif inlineAsmSyntax.len == 0: + raiseAssert "Your compiler does not support the specified inline assembler" + let isBasicAsm = ( fetchInfo(t, n, IsGlobal).infoVal(t, bool) or fetchInfo(t, n, InPure).infoVal(t, bool) - ) and c.props.inlineAsmSyntax != VisualCPP + ) and inlineAsmSyntax != {VisualCPP} if not isBasicAsm: - case c.props.inlineAsmSyntax: - of None: raiseAssert "Your compiler does not support the inline assembler" - of GCCExtendedAsm: genGccAsm(c, t, code) - of VisualCPP: genVisualCPPAsm(c, t, code) + if inlineAsmSyntax == {GCCExtendedAsm}: genGccAsm(c, t, code) + elif inlineAsmSyntax == {VisualCPP}: genVisualCPPAsm(c, t, code) + else: raiseAssert "Not implemented inline asm syntax" else: assert ( isLastSon(t, code, code.firstSon) and diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim index ccf8c2f3ad64b..3a6e96f40e114 100644 --- a/compiler/nir/nirc.nim +++ b/compiler/nir/nirc.nim @@ -30,7 +30,7 @@ proc writeHelp = proc main = var inp = "" var cmd = "" - var props = TargetProps(inlineAsmSyntax: GCCExtendedAsm) + var props = TargetProps() for kind, key, val in getopt(): case kind @@ -46,14 +46,13 @@ proc main = if val == "": quit "Error: no inline asm syntax specified" - props.inlineAsmSyntax = + props.inlineAsmSyntax.incl( case val: - of "none": None - of "gcc-like": GCCExtendedAsm - of "msvc-like": VisualCPP + of "gcc": GCCExtendedAsm + of "vcc": VisualCPP else: - quit "Error: invalid inline asm syntax. Must be: gcc-like or msvc-like (or none)" - + quit "Error: invalid inline asm syntax. Must be: gcc or vcc (or none)" + ) of cmdEnd: discard if inp.len == 0: quit "Error: no input file specified" diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 914a3a1cec3a7..39d7b4fe82f46 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -132,6 +132,7 @@ type InfoKey* = enum IsGlobal InPure + InlineAsmSyntax EmitTargetKind* = enum Asm @@ -415,8 +416,14 @@ proc infoVal*(info: NodePos, tree: Tree, t: type bool): bool = let (key, b) = sons2(tree, info) assert cast[InfoKey](tree[key].operand) in boolInfos, "Info must be in bool infos" - cast[bool](tree[b].operand) + cast[bool](tree[b].immediateVal) +import target_props +proc infoVal*(info: NodePos, tree: Tree, t: type InlineAsmSyntaxKind): InlineAsmSyntaxKind = + assert tree[info].kind == Info + let (key, e) = sons2(tree, info) + + cast[InlineAsmSyntaxKind](tree[e].immediateVal) type LabelId* = distinct int @@ -510,6 +517,11 @@ proc addBoolInfo*(t: var Tree; info: PackedLineInfo; k: InfoKey, b: bool) = t.addInfoId info, k t.addImmediateVal info, b.int +proc addEnumInfo*[T: enum](t: var Tree; info: PackedLineInfo; k: InfoKey, e: T) = + build t, info, Info: + t.addInfoId info, k + t.addImmediateVal info, e.ord + proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes diff --git a/compiler/nir/target_props.nim b/compiler/nir/target_props.nim index 7d30ce221252f..451c431344fb8 100644 --- a/compiler/nir/target_props.nim +++ b/compiler/nir/target_props.nim @@ -1,8 +1,7 @@ type - InlineAsmSyntax* = enum - None + InlineAsmSyntaxKind* = enum GCCExtendedAsm VisualCPP TargetProps* = object - inlineAsmSyntax*: InlineAsmSyntax + inlineAsmSyntax*: set[InlineAsmSyntaxKind] # for example in icc {GCCExtendedAsm, VisualCPP} diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 35b4d63c28806..222d320aaa2e4 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -153,20 +153,6 @@ proc pragmaEnsures(c: PContext, n: PNode) = n[1] = c.semExpr(c, n[1]) closeScope(c) -proc pragmaAsm*(c: PContext, n: PNode): char = - result = '\0' - if n != nil: - for i in 0.. Date: Wed, 13 Dec 2023 17:25:29 +0300 Subject: [PATCH 26/29] simple dialect switch --- compiler/nir/nirc.nim | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim index 3a6e96f40e114..4072ed9c25794 100644 --- a/compiler/nir/nirc.nim +++ b/compiler/nir/nirc.nim @@ -51,8 +51,20 @@ proc main = of "gcc": GCCExtendedAsm of "vcc": VisualCPP else: - quit "Error: invalid inline asm syntax. Must be: gcc or vcc (or none)" + quit "Error: invalid inline asm syntax. Must be: gcc or vcc" ) + of "baseDialect", "dialect": + if val == "": + quit "Error: no base dialect specified" + + props = + # default properties for selected base dialect + case val: + of "gcc": TargetProps(inlineAsmSyntax: {GCCExtendedAsm}) + of "vcc": TargetProps(inlineAsmSyntax: {VisualCPP}) + of "icc": TargetProps(inlineAsmSyntax: {GCCExtendedAsm, VisualCPP}) + else: + quit "Error: invalid base dialect. Must be in {gcc, vcc, icc}" of cmdEnd: discard if inp.len == 0: quit "Error: no input file specified" From 5d6c35465b3b76fe6e73147702d050fb91d8af60 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Wed, 13 Dec 2023 18:50:59 +0300 Subject: [PATCH 27/29] Update cir.nim --- compiler/nir/cir.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 82110c86ef4a7..80a0a77ddd8a5 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -890,14 +890,15 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = if inlineAsmSyntax.len > 1: raiseAssert "Ambiguous asm syntax, please specify via inlineAsmSyntax pragma" - elif inlineAsmSyntax.len == 0: - raiseAssert "Your compiler does not support the specified inline assembler" - + let isBasicAsm = ( fetchInfo(t, n, IsGlobal).infoVal(t, bool) or fetchInfo(t, n, InPure).infoVal(t, bool) ) and inlineAsmSyntax != {VisualCPP} + if inlineAsmSyntax.len == 0 and not isBasicAsm: + raiseAssert "Your compiler does not support the specified inline assembler" + if not isBasicAsm: if inlineAsmSyntax == {GCCExtendedAsm}: genGccAsm(c, t, code) elif inlineAsmSyntax == {VisualCPP}: genVisualCPPAsm(c, t, code) From f0efd4c8f727095686a5acfc514ae91fa635325d Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Wed, 13 Dec 2023 19:40:04 +0300 Subject: [PATCH 28/29] Update cir.nim --- compiler/nir/cir.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 80a0a77ddd8a5..6273d7982a65c 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -877,7 +877,7 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case target: of Asm: - requireInfo t, n, InPure + requireInfo t, n, {InPure, IsGlobal} # Resolve asm overloads (as example for icc inlineAsmSyntax pragma must be specified) var inlineAsmSyntax = c.props.inlineAsmSyntax From 811e6abdc22ee181a16c9deb223dc79d2a273051 Mon Sep 17 00:00:00 2001 From: ASVIEST Date: Wed, 13 Dec 2023 19:43:07 +0300 Subject: [PATCH 29/29] Revert "Update cir.nim" This reverts commit f0efd4c8f727095686a5acfc514ae91fa635325d. --- compiler/nir/cir.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim index 6273d7982a65c..80a0a77ddd8a5 100644 --- a/compiler/nir/cir.nim +++ b/compiler/nir/cir.nim @@ -877,7 +877,7 @@ proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = case target: of Asm: - requireInfo t, n, {InPure, IsGlobal} + requireInfo t, n, InPure # Resolve asm overloads (as example for icc inlineAsmSyntax pragma must be specified) var inlineAsmSyntax = c.props.inlineAsmSyntax