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 5 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
12 changes: 12 additions & 0 deletions compiler/nir/ast2ir.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,17 @@ proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) =
else:
genCall c, n, d

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):
setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
Expand Down Expand Up @@ -2560,6 +2571,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
6 changes: 6 additions & 0 deletions compiler/nir/cir.nim
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type
CaseKeyword = "case "
DefaultKeyword = "default:"
BreakKeyword = "break"
AsmKeyword = "__asm__ "
NullPtr = "nullptr"
IfNot = "if (!("
ReturnKeyword = "return "
Expand Down Expand Up @@ -812,6 +813,11 @@ 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 Verbatim:
discard
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

const
Prelude = """
Expand Down
314 changes: 314 additions & 0 deletions compiler/nir/gcc_extended_asm.nim
Original file line number Diff line number Diff line change
@@ -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
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 we write ASTs in Nim 2023. We now use "packed trees". The files you have touched can guide you.

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
Loading