Skip to content

NIR: asm statement support, Wip #22992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ddaea3b
Asm (gcc extended asm) for nir
ASVIEST Nov 27, 2023
73eb391
Update genasm.nim
ASVIEST Nov 27, 2023
73f9b60
Asm parsing now working on backends (WIP(
ASVIEST Nov 29, 2023
a4ddb3b
Emit targets
ASVIEST Nov 29, 2023
03af260
Remove asm instrs
ASVIEST Nov 29, 2023
545d35a
Update nirinsts.nim
ASVIEST Dec 1, 2023
5cefc65
verbatims
ASVIEST Dec 1, 2023
cd5de29
gcc asm (simple)
ASVIEST Dec 2, 2023
c0ab580
fix lastSon
ASVIEST Dec 2, 2023
78a4c61
Seperated file
ASVIEST Dec 2, 2023
e4af3f0
target properties
ASVIEST Dec 2, 2023
e9221ae
GCC asm c codegen
ASVIEST Dec 2, 2023
8419151
Small fix
ASVIEST Dec 2, 2023
57802ed
apply suggestions
ASVIEST Dec 3, 2023
a1f9c3f
msvc asm
ASVIEST Dec 3, 2023
f41557d
info's
ASVIEST Dec 3, 2023
fc8b91f
fix
ASVIEST Dec 3, 2023
68bc65d
fix
ASVIEST Dec 3, 2023
3d4f45e
Extended asm is now default inline asm syntax
ASVIEST Dec 3, 2023
119e15b
Merge branch 'devel' into nir-asm
ASVIEST Dec 3, 2023
24b6c55
support asmNoStackFrame
ASVIEST Dec 3, 2023
bb463f1
Merge branch 'nir-asm' of https://github.com/ASVIEST/Nim into nir-asm
ASVIEST Dec 3, 2023
5f58ec9
use strings for verbatims
ASVIEST Dec 4, 2023
330ae88
fix asmNoStackFrame
ASVIEST Dec 4, 2023
21ee838
fix
ASVIEST Dec 4, 2023
cc45710
small refactor
ASVIEST Dec 7, 2023
1b88194
Merge branch 'nim-lang:devel' into nir-asm
ASVIEST Dec 7, 2023
c7d3093
Merge branch 'nim-lang:devel' into nir-asm
ASVIEST Dec 12, 2023
11a6fe8
inlineAsmSyntax pragma
ASVIEST Dec 12, 2023
ec8beea
Merge branch 'nim-lang:devel' into nir-asm
ASVIEST Dec 12, 2023
540ef8f
simple dialect switch
ASVIEST Dec 13, 2023
5d6c354
Update cir.nim
ASVIEST Dec 13, 2023
f0efd4c
Update cir.nim
ASVIEST Dec 13, 2023
811e6ab
Revert "Update cir.nim"
ASVIEST Dec 13, 2023
94c7fa0
Merge branch 'nim-lang:devel' into nir-asm
ASVIEST Dec 13, 2023
747ead5
Merge branch 'nim-lang:devel' into nir-asm
ASVIEST Dec 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions compiler/nir/ast2ir.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -2463,7 +2464,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:
Expand Down Expand Up @@ -2517,6 +2518,53 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) =
else:
genCall c, n, d

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 offset..<n.len:
let it = n[i]
case it.kind:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not how the rest of the codebase indents the case statement.

of nkStrLit..nkTripleStrLit:
c.code.addVerbatim c.lit.strings, info, it.strVal
of nkSym:
c.code.addSymUse info, toSymId(c, it.sym)
else:
gen(c, it)

import target_props
proc genAsm(c: var ProcCon; n: PNode) =
let info = toLineInfo(c, n.info)

var inlineAsmSyntax = ""
if (let p = n[0]; p).kind == nkPragma:
for i in p:
if whichPragma(i) == wInlineAsmSyntax:
inlineAsmSyntax = i[1].strVal

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
)

if inlineAsmSyntax != "":
c.code.addEnumInfo info, InlineAsmSyntax, (
case inlineAsmSyntax:
of "gcc": GCCExtendedAsm
of "vcc": VisualCPP
else: raiseAssert "never"
)

genEmitCode c, n, true


proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
when defined(nimCompilerStacktraceHints):
setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
Expand Down Expand Up @@ -2560,6 +2608,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: genAsm(c, n)
of nkReturnStmt: genReturn(c, n)
of nkRaiseStmt: genRaise(c, n)
of nkBreakStmt: genBreak(c, n)
Expand Down
127 changes: 121 additions & 6 deletions compiler/nir/cir.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -31,6 +32,7 @@ type
Semicolon = ";"
Comma = ", "
Space = " "
Quote = $'"'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Quote = $'"'
Quote = "\""

Colon = ": "
Dot = "."
Arrow = "->"
Expand Down Expand Up @@ -63,6 +65,7 @@ proc fillTokenTable(tab: var BiTable[string]) =
type
GeneratedCode* = object
m: NirModule
props: TargetProps
includes: seq[LitId]
includedHeaders: IntSet
data: seq[LitId]
Expand All @@ -75,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.} =
Expand Down Expand Up @@ -444,7 +450,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

Expand Down Expand Up @@ -554,6 +562,54 @@ 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 "__asm__ "
c.add ParLe
c.add NewLine

var asmTemplate = true
var left = 0
var s = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is s mutable?


template maybeAddQuote =
if asmTemplate: c.add Quote

template findSecUpdate(s: string): bool =
var result = false
for i in s:
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':
c.add s[left..i - 1]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What? Why? Didn't you write a parser for the asm? Shouldn't it split it at newlines if they are important?

if asmTemplate: c.add r"\n"
maybeAddQuote
left = i + 1
c.add NewLine
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 findSecUpdate(s):
c.add s[left..^1]
maybeAddQuote
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:
Expand Down Expand Up @@ -807,11 +863,65 @@ 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:
c.add c.m.lit.strings[t[n].litId]
of Emit:
requireInfo t, n, IsGlobal
let
target = cast[EmitTargetKind](t[n.firstSon].rawOperand)
code = lastSon(t, n)

case target:
of Asm:
requireInfo t, n, InPure

# Resolve asm overloads (as example for icc inlineAsmSyntax pragma must be specified)
var inlineAsmSyntax = c.props.inlineAsmSyntax
if haveInfo(t, n, InlineAsmSyntax):
inlineAsmSyntax = inlineAsmSyntax * {
fetchInfo(t, n, InlineAsmSyntax).infoVal(
t, InlineAsmSyntaxKind
)
}

if inlineAsmSyntax.len > 1:
raiseAssert "Ambiguous asm syntax, please specify via inlineAsmSyntax pragma"

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)
else: raiseAssert "Not implemented inline asm syntax"
else:
assert (
isLastSon(t, code, code.firstSon) and
t[code.firstSon].kind == Verbatim
), "Invalid basic asm. Basic asm should be only one verbatim"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the frontend do this?

genGccAsm(c, t, code)

of Code:
raiseAssert"not supported"

of EmitTarget:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a EmitTarget an opcode of its own? We have a pragma annotation system in place for this...

discard

of EmitCode:
for ch in sons(t, n):
gen c, t, ch

of Info, InfoId:
discard "Info can't generate code"

const
Prelude = """
Expand Down Expand Up @@ -940,6 +1050,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) =
Expand All @@ -958,8 +1073,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)
Expand Down
Loading