Skip to content

Commit e1db43f

Browse files
committed
fixes - can self-compile with ORC!
These fixes bring ORC to a point where `nlvm` can compile itself - there's a lot of bloat in the ORC code however, the end result being a binary that's 15% slower for now. ORC also breaks allocation elision, likely because the allocation functions have become too complex - this can perhaps be solved with some optimization pass reordering and/or selective early inlining, but it remains to be seen - first, excessive EH from range-checked pointer arithmetic must be addressed. * mark several C functions as `nounwind` to reduce EH bloat * better allocation / deallocation annotations * fix missing `wasMoved` calls
1 parent 9a36854 commit e1db43f

File tree

3 files changed

+163
-53
lines changed

3 files changed

+163
-53
lines changed

llvm/llvm.nim

+1
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ let
622622
attrAllockind* = getEnumAttrKind("allockind")
623623
attrAllocsize* = getEnumAttrKind("allocsize")
624624
attrAllocptr* = getEnumAttrKind("allocptr")
625+
attrAllocalign* = getEnumAttrKind("allocalign")
625626
attrAlign* = getEnumAttrKind("align")
626627
attrNonnull* = getEnumAttrKind("nonnull")
627628
attrNoalias* = getEnumAttrKind("noalias")

nlvm/llgen.nim

+161-52
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ type
164164
# Same as cgen, for easier genAssignment comparison
165165
TAssignmentFlag = enum
166166
needToCopy
167+
needToCopySinkParam
167168

168169
TAssignmentFlags = set[TAssignmentFlag]
169170

@@ -4734,6 +4735,29 @@ proc callAssign(
47344735
fty = f.globalGetValueType()
47354736
discard g.b.buildCall2(fty, f, [dest, src, shallow], "")
47364737

4738+
const noUnwinds = toHashSet(
4739+
[
4740+
("ansi_c", "c_memchr"),
4741+
("ansi_c", "c_memcmp"),
4742+
("ansi_c", "c_memcpy"),
4743+
("ansi_c", "c_memmove"),
4744+
("ansi_c", "c_memset"),
4745+
("ansi_c", "c_strcmp"),
4746+
("ansi_c", "c_strlen"),
4747+
("ansi_c", "c_strstr"),
4748+
("ansi_c", "c_abort"),
4749+
("ansi_c", "c_malloc"),
4750+
("ansi_c", "c_calloc"),
4751+
("ansi_c", "c_free"),
4752+
("ansi_c", "c_realloc"),
4753+
("nlvm_system", "mmap"),
4754+
("nlvm_system", "munmap"),
4755+
("system", "mmap"),
4756+
("system", "munmap"),
4757+
("system", "rawQuit"),
4758+
]
4759+
)
4760+
47374761
proc addNimFunction(g: LLGen, sym: PSym): llvm.ValueRef =
47384762
## Add a function prototype for a nim function to the given module
47394763
let
@@ -4743,6 +4767,18 @@ proc addNimFunction(g: LLGen, sym: PSym): llvm.ValueRef =
47434767

47444768
f = g.addNimFunction(name, ty)
47454769

4770+
if sfImportc in sym.flags:
4771+
if typ[0] != nil and typ[0].kind in {tyTuple, tyObject} and
4772+
g.config.getSize(typ[0]) <= 16:
4773+
# TODO https://github.com/llvm/llvm-project/blob/6cbc64ed922cc69bc292d394ba5c681fa309f404/clang/lib/CodeGen/Targets/X86.cpp#L1783
4774+
# TODO https://github.com/ziglang/zig/pull/9443
4775+
g.config.message(
4776+
sym.info,
4777+
warnUser,
4778+
"TODO: C ABI for small struct returns not implemented - there may be issues: " &
4779+
$name,
4780+
)
4781+
47464782
if sfNoReturn in sym.flags:
47474783
f.addFuncAttribute(g.attrNoReturn)
47484784

@@ -4761,64 +4797,94 @@ proc addNimFunction(g: LLGen, sym: PSym): llvm.ValueRef =
47614797
]:
47624798
f.addFuncAttribute(g.attrCold)
47634799

4764-
if (sym.originatingModule.name.s, sym.name.s) in
4765-
[("system", "quit"), ("ansi_c", "c_abort")]:
4800+
# C functions known to not raise exceptions - can't enable for all importc
4801+
# functions because they might take a callback or use a global to cause
4802+
# unwinding :/
4803+
if (sym.originatingModule.name.s, sym.name.s) in noUnwinds:
47664804
f.addFuncAttribute(g.attrNoUnwind)
47674805

47684806
if sym.originatingModule.name.s == "system" and g.config.selectedGC notin {gcRegions}:
4769-
if sym.name.s in ["allocImpl", "allocSharedImpl"]:
4807+
template allocsize(size, count: uint32): uint64 =
4808+
uint64(size) shl 32 or uint64(count)
4809+
4810+
template allocsize(size: uint32): uint64 =
4811+
allocsize(size, not 0'u32)
4812+
4813+
# TODO this constant should be set based on the _target_, not `nlvm` itself
4814+
const MemAlign =
4815+
when defined(nimMemAlignTiny):
4816+
4
4817+
elif defined(useMalloc):
4818+
when defined(amd64): 16 else: 8
4819+
else:
4820+
16
4821+
4822+
const allocDefaultAlignedFns = toTable(
4823+
{
4824+
"allocImpl": (AllocFnKindUninitialized, allocsize(0)),
4825+
"allocSharedImpl": (AllocFnKindUninitialized, allocsize(0)),
4826+
"alloc0Impl": (AllocFnKindZeroed, allocsize(0)),
4827+
"allocShared0Impl": (AllocFnKindZeroed, allocsize(0)),
4828+
"newObj": (AllocFnKindZeroed, allocsize(1)),
4829+
"newObjRC1": (AllocFnKindZeroed, allocsize(1)),
4830+
"newObjNoInit": (AllocFnKindUninitialized, allocsize(1)),
4831+
"rawNewObj": (AllocFnKindUninitialized, allocsize(1)),
4832+
"alloc": (AllocFnKindUninitialized, allocsize(1)),
4833+
"rawAlloc": (AllocFnKindUninitialized, allocsize(1)),
4834+
}
4835+
)
4836+
4837+
const allocParamAlignedFns = toTable(
4838+
{
4839+
"nimNewObj": (AllocFnKindZeroed, allocsize(0), 1),
4840+
"nimNewObjUninit": (AllocFnKindUninitialized, allocsize(0), 1),
4841+
"alignedAlloc": (AllocFnKindUninitialized, allocsize(0), 1),
4842+
"alignedAlloc0": (AllocFnKindZeroed, allocsize(0), 1),
4843+
}
4844+
)
4845+
4846+
if sym.name.s in allocDefaultAlignedFns:
4847+
let attrs = allocDefaultAlignedFns[sym.name.s]
47704848
f.addFuncAttribute(g.lc.createStringAttribute("alloc-family", "nimgc"))
47714849
f.addFuncAttribute(
4772-
g.lc.createEnumAttribute(
4773-
attrAllockind, AllocFnKindAlloc or AllocFnKindUninitialized
4774-
)
4850+
g.lc.createEnumAttribute(attrAllockind, AllocFnKindAlloc or attrs[0])
47754851
)
4776-
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, cast[uint32](-1)))
4852+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, attrs[1]))
47774853
f.addAttributeAtIndex(
4778-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4854+
AttributeIndex(AttributeReturnIndex),
4855+
g.lc.createEnumAttribute(attrAlign, MemAlign),
47794856
)
47804857
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
47814858
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
4782-
elif sym.name.s in ["alloc0Impl", "allocShared0Impl"]:
4859+
elif sym.name.s in allocParamAlignedFns:
4860+
let attrs = allocParamAlignedFns[sym.name.s]
47834861
f.addFuncAttribute(g.lc.createStringAttribute("alloc-family", "nimgc"))
47844862
f.addFuncAttribute(
4785-
g.lc.createEnumAttribute(attrAllockind, AllocFnKindAlloc or AllocFnKindZeroed)
4863+
g.lc.createEnumAttribute(
4864+
attrAllockind, AllocFnKindAlloc or AllocFnKindAligned or attrs[0]
4865+
)
47864866
)
4787-
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, cast[uint32](-1)))
4867+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, attrs[1]))
47884868
f.addAttributeAtIndex(
4789-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4869+
AttributeIndex(attrs[2] + 1), g.lc.createEnumAttribute(attrAllocalign, 0)
47904870
)
47914871
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
47924872
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
4793-
elif sym.name.s in ["alloc0", "newObj", "newObjRC1", "rawAlloc0", "allocShared0"]:
4873+
elif sym.name.s in ["dealloc", "deallocShared"]:
4874+
f.addFuncAttribute(g.attrNoUnwind)
47944875
f.addFuncAttribute(g.lc.createStringAttribute("alloc-family", "nimgc"))
4795-
f.addFuncAttribute(
4796-
g.lc.createEnumAttribute(attrAllockind, AllocFnKindAlloc or AllocFnKindZeroed)
4797-
)
4798-
f.addFuncAttribute(
4799-
g.lc.createEnumAttribute(attrAllocsize, uint64(1 shl 32) + cast[uint32](-1))
4800-
)
4876+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllockind, AllocFnKindFree))
4877+
48014878
f.addAttributeAtIndex(
4802-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4879+
AttributeIndex(typ.len - 1), g.lc.createEnumAttribute(attrAllocptr, 0)
48034880
)
4804-
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
4805-
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
4806-
elif sym.name.s in
4807-
["alloc", "rawAlloc", "newObjNoInit", "rawNewObj", "rawAlloc", "allocShared"]:
4881+
elif sym.name.s in ["rawDealloc"]:
4882+
f.addFuncAttribute(g.attrNoUnwind)
48084883
f.addFuncAttribute(g.lc.createStringAttribute("alloc-family", "nimgc"))
4809-
f.addFuncAttribute(
4810-
g.lc.createEnumAttribute(
4811-
attrAllockind, AllocFnKindAlloc or AllocFnKindUninitialized
4812-
)
4813-
)
4814-
f.addFuncAttribute(
4815-
g.lc.createEnumAttribute(attrAllocsize, uint64(1 shl 32) + cast[uint32](-1))
4816-
)
4884+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllockind, AllocFnKindFree))
48174885
f.addAttributeAtIndex(
4818-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4886+
AttributeIndex(2), g.lc.createEnumAttribute(attrAllocptr, 0)
48194887
)
4820-
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
4821-
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
48224888

48234889
f
48244890

@@ -5388,7 +5454,7 @@ proc genBoundsCheckArray(g: LLGen, arr, firstOrd, lastOrd, a, b: llvm.ValueRef)
53885454

53895455
proc buildLoadVar(g: LLGen, typ: PType, v: llvm.ValueRef): llvm.ValueRef =
53905456
if typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
5391-
g.b.buildLoad2(g.ptrTy, v)
5457+
g.b.buildLoad2(g.llType(last(typ.skipTypes(abstractInst))), v)
53925458
else:
53935459
v
53945460

@@ -5427,14 +5493,12 @@ proc genOpenArrayConv(
54275493
else:
54285494
axp.v
54295495
v = g.buildLoadVar(n.typ, ax)
5430-
54315496
(g.getNimSeqDataPtr(seqTy, v), g.loadNimSeqLen(v))
54325497
of tyOpenArray, tyVarargs:
5433-
let ax = g.buildLoadVar(n.typ, g.genNode(n, false).v)
5434-
5498+
let ax = g.buildLoadVar(n.typ, g.genNode(n, true).v)
54355499
(
5436-
g.b.buildLoad2(g.ptrTy, g.buildOpenArrayDataGEP(ax)),
5437-
g.b.buildLoad2(g.intTy, g.buildOpenArrayLenGEP(ax)),
5500+
g.b.buildExtractValue(ax, 0, g.nn("p", ax)),
5501+
g.b.buildExtractValue(ax, 1, g.nn("l", ax)),
54385502
)
54395503
of tyArray, tyUncheckedArray:
54405504
var v = g.genNode(n, true).v
@@ -5798,9 +5862,8 @@ proc genAssignment(g: LLGen, dest, src: LLValue, typ: PType, flags: TAssignmentF
57985862
of tyString:
57995863
if optSeqDestructors in g.config.globalOptions:
58005864
discard g.b.buildStore(src.v, dest.v)
5801-
elif ({needToCopy} * flags == {} and src.storage != OnStatic) or canMove(
5802-
g, src.lode
5803-
):
5865+
elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or
5866+
canMove(g, src.lode):
58045867
g.genRefAssign(dest, src.v)
58055868
else:
58065869
if (dest.storage == OnStack and g.config.selectedGC != gcGo) or
@@ -5961,9 +6024,15 @@ proc genConstBracket(g: LLGen, n: PNode): llvm.ValueRef =
59616024
for i, s in n.sons:
59626025
vals[i] = g.genConstInitializer(s)
59636026
let s = constArray(et, vals)
5964-
if typ.kind in {tyArray, tyUncheckedArray}:
6027+
case typ.kind
6028+
of tyArray, tyUncheckedArray:
59656029
s
5966-
else:
6030+
of tyOpenArray:
6031+
let lit = g.m.addPrivateConstant(s.typeOfX, g.nn(".oa", n))
6032+
lit.setInitializer(s)
6033+
6034+
llvm.constNamedStruct(g.llOpenArrayType(), [lit, g.constInt64(vals.len)])
6035+
of tySequence:
59676036
let
59686037
ll = g.constNimInt(vals.len)
59696038
cap = g.constNimInt(vals.len + g.strLitFlag)
@@ -5980,6 +6049,8 @@ proc genConstBracket(g: LLGen, n: PNode): llvm.ValueRef =
59806049
lit = g.m.addPrivateConstant(payload.typeOfX, g.nn(".seq", n))
59816050
lit.setInitializer(payload)
59826051
lit
6052+
else:
6053+
raiseAssert "Unexpected const bracket: " & $typ.kind
59836054

59846055
proc genConstObjConstr(g: LLGen, n: PNode): llvm.ValueRef =
59856056
let
@@ -6748,6 +6819,8 @@ proc genMagicLengthStr(g: LLGen, n: PNode): LLValue =
67486819
fty = llvm.functionType(g.csizetTy, [g.primitives[tyCString]])
67496820
f = g.m.getOrInsertFunction("strlen", fty)
67506821

6822+
f.addFuncAttribute(g.attrNoUnwind)
6823+
67516824
let v1 = g.buildTruncOrExt(
67526825
g.b.buildCall2(fty, f, [v], g.nn("str.len.call", n)), g.primitives[tyInt], false
67536826
)
@@ -7790,13 +7863,49 @@ proc genMagicMove(g: LLGen, n: PNode, load: bool): LLValue =
77907863

77917864
g.buildStoreNull(ty, tmpx.v)
77927865
g.genObjectInit(n[1].typ, tmpx.v)
7793-
let flags =
7794-
if not canMove(g, n[1]):
7795-
{needToCopy}
7866+
if g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
7867+
g.genAssignment(tmpx, g.maybeLoadValue(ty, ax, lx), n[1].typ, {})
7868+
var op = getAttachedOp(g.graph, n.typ, attachedWasMoved)
7869+
if op == nil:
7870+
g.callReset(n[1].skipAddr.typ, ax)
77967871
else:
7797-
{}
7798-
g.genAssignment(tmpx, g.maybeLoadValue(ty, ax, lx), n[1].typ, flags)
7799-
g.callReset(n[1].skipAddr.typ, ax)
7872+
case skipTypes(n[1].skipAddr.typ, abstractVar + {tyStatic}).kind
7873+
of tyOpenArray, tyVarargs:
7874+
# todo fixme generated `wasMoved` hooks for
7875+
# openarrays, but it probably shouldn't?
7876+
raiseAssert "TODO"
7877+
# var s: string
7878+
# if reifiedOpenArray(a.lode):
7879+
# if a.t.kind in {tyVar, tyLent}:
7880+
# s = "$1->Field0, $1->Field1" % [rdLoc(a)]
7881+
# else:
7882+
# s = "$1.Field0, $1.Field1" % [rdLoc(a)]
7883+
# else:
7884+
# s = "$1, $1Len_0" % [rdLoc(a)]
7885+
# linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s])
7886+
else:
7887+
let
7888+
f = g.genFunctionWithBody(op).v
7889+
fty = f.globalGetValueType()
7890+
discard g.b.buildCall2(fty, f, [ax.v], "")
7891+
else:
7892+
if n[1].kind == nkSym and isSinkParam(n[1].sym):
7893+
let ty2 = g.llType(n[1].typ.skipTypes({tySink}))
7894+
let tmp2 =
7895+
LLValue(v: g.localAlloca(ty2, g.nn("move.tmp2", n[1])), storage: OnStack)
7896+
7897+
g.buildStoreNull(ty2, tmp2.v)
7898+
g.genObjectInit(n[1].typ.skipTypes({tySink}), tmp2.v)
7899+
7900+
g.genAssignment(
7901+
tmp2, g.maybeLoadValue(ty, ax, lx), n[1].typ, {needToCopySinkParam}
7902+
)
7903+
g.genAssignment(tmpx, g.buildLoadValue(ty2, tmp2), n[1].typ, {})
7904+
g.callReset(n[1].typ.skipTypes({tySink}), tmp2)
7905+
else:
7906+
g.genAssignment(tmpx, g.maybeLoadValue(ty, ax, lx), n[1].typ, {})
7907+
g.callReset(n[1].skipAddr.typ, ax)
7908+
78007909
g.maybeLoadValue(ty, tmpx, load)
78017910

78027911
proc genMagicDestroy(g: LLGen, n: PNode) =

skipped-tests.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ tests/pragmas/tpush.nim c
160160
tests/pragmas/treorder.nim c
161161
tests/realtimeGC/tmain.nim c
162162
tests/sets/t20997.nim c
163+
tests/stdlib/tasynchttpserver_transferencoding.nim c --mm:arc -d:danger
163164
tests/stdlib/tcasts.nim c
164165
tests/stdlib/thashes.nim c --backend:js --jsbigint64:off
165166
tests/stdlib/thashes.nim c --backend:js --jsbigint64:on
@@ -195,7 +196,6 @@ tests/threads/tonthreadcreation.nim c --threads:on --mm:orc --deepcopy:on
195196
tests/threads/tonthreadcreation.nim c --threads:on --tlsEmulation:on --mm:orc --deepcopy:on
196197
tests/tools/tnimgrep.nim c
197198
tests/views/tcan_compile_nim.nim c
198-
tests/views/tconst_views.nim c
199199
tests/views/tsplit_into_seq.nim c
200200
tests/vm/tvmmisc.nim c
201201
tests/vm/tvmops.nim c

0 commit comments

Comments
 (0)