Skip to content

Commit 89db076

Browse files
authored
fixes - can self-compile with ORC! (#74)
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 89db076

File tree

3 files changed

+169
-53
lines changed

3 files changed

+169
-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

+167-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,31 @@ proc callAssign(
47344735
fty = f.globalGetValueType()
47354736
discard g.b.buildCall2(fty, f, [dest, src, shallow], "")
47364737

4738+
# TODO using a constant here results in compile-time differences in hash member
4739+
# order which is weird
4740+
let noUnwinds = toHashSet(
4741+
[
4742+
("ansi_c", "c_memchr"),
4743+
("ansi_c", "c_memcmp"),
4744+
("ansi_c", "c_memcpy"),
4745+
("ansi_c", "c_memmove"),
4746+
("ansi_c", "c_memset"),
4747+
("ansi_c", "c_strcmp"),
4748+
("ansi_c", "c_strlen"),
4749+
("ansi_c", "c_strstr"),
4750+
("ansi_c", "c_abort"),
4751+
("ansi_c", "c_malloc"),
4752+
("ansi_c", "c_calloc"),
4753+
("ansi_c", "c_free"),
4754+
("ansi_c", "c_realloc"),
4755+
("nlvm_system", "mmap"),
4756+
("nlvm_system", "munmap"),
4757+
("system", "mmap"),
4758+
("system", "munmap"),
4759+
("system", "rawQuit"),
4760+
]
4761+
)
4762+
47374763
proc addNimFunction(g: LLGen, sym: PSym): llvm.ValueRef =
47384764
## Add a function prototype for a nim function to the given module
47394765
let
@@ -4743,6 +4769,18 @@ proc addNimFunction(g: LLGen, sym: PSym): llvm.ValueRef =
47434769

47444770
f = g.addNimFunction(name, ty)
47454771

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

@@ -4761,64 +4799,98 @@ proc addNimFunction(g: LLGen, sym: PSym): llvm.ValueRef =
47614799
]:
47624800
f.addFuncAttribute(g.attrCold)
47634801

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

47684808
if sym.originatingModule.name.s == "system" and g.config.selectedGC notin {gcRegions}:
4769-
if sym.name.s in ["allocImpl", "allocSharedImpl"]:
4809+
template allocsize(size, count: uint32): uint64 =
4810+
uint64(size) shl 32 or uint64(count)
4811+
4812+
template allocsize(size: uint32): uint64 =
4813+
allocsize(size, not 0'u32)
4814+
4815+
# TODO this constant should be set based on the _target_, not `nlvm` itself
4816+
const MemAlign =
4817+
when defined(nimMemAlignTiny):
4818+
4
4819+
elif defined(useMalloc):
4820+
when defined(amd64): 16 else: 8
4821+
else:
4822+
16
4823+
4824+
# TODO using a constant here results in compile-time differences in hash member
4825+
# order which is weird
4826+
let allocDefaultAlignedFns = toTable(
4827+
{
4828+
"allocImpl": (AllocFnKindUninitialized, allocsize(0)),
4829+
"allocSharedImpl": (AllocFnKindUninitialized, allocsize(0)),
4830+
"alloc0Impl": (AllocFnKindZeroed, allocsize(0)),
4831+
"allocShared0Impl": (AllocFnKindZeroed, allocsize(0)),
4832+
"newObj": (AllocFnKindZeroed, allocsize(1)),
4833+
"newObjRC1": (AllocFnKindZeroed, allocsize(1)),
4834+
"newObjNoInit": (AllocFnKindUninitialized, allocsize(1)),
4835+
"rawNewObj": (AllocFnKindUninitialized, allocsize(1)),
4836+
"alloc": (AllocFnKindUninitialized, allocsize(1)),
4837+
"rawAlloc": (AllocFnKindUninitialized, allocsize(1)),
4838+
}
4839+
)
4840+
4841+
# TODO using a constant here results in compile-time differences in hash member
4842+
# order which is weird
4843+
let allocParamAlignedFns = toTable(
4844+
{
4845+
"nimNewObj": (AllocFnKindZeroed, allocsize(0), 1),
4846+
"nimNewObjUninit": (AllocFnKindUninitialized, allocsize(0), 1),
4847+
"alignedAlloc": (AllocFnKindUninitialized, allocsize(0), 1),
4848+
"alignedAlloc0": (AllocFnKindZeroed, allocsize(0), 1),
4849+
}
4850+
)
4851+
4852+
if sym.name.s in allocDefaultAlignedFns:
4853+
let attrs = allocDefaultAlignedFns[sym.name.s]
47704854
f.addFuncAttribute(g.lc.createStringAttribute("alloc-family", "nimgc"))
47714855
f.addFuncAttribute(
4772-
g.lc.createEnumAttribute(
4773-
attrAllockind, AllocFnKindAlloc or AllocFnKindUninitialized
4774-
)
4856+
g.lc.createEnumAttribute(attrAllockind, AllocFnKindAlloc or attrs[0])
47754857
)
4776-
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, cast[uint32](-1)))
4858+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, attrs[1]))
47774859
f.addAttributeAtIndex(
4778-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4860+
AttributeIndex(AttributeReturnIndex),
4861+
g.lc.createEnumAttribute(attrAlign, MemAlign),
47794862
)
47804863
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
47814864
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
4782-
elif sym.name.s in ["alloc0Impl", "allocShared0Impl"]:
4865+
elif sym.name.s in allocParamAlignedFns:
4866+
let attrs = allocParamAlignedFns[sym.name.s]
47834867
f.addFuncAttribute(g.lc.createStringAttribute("alloc-family", "nimgc"))
47844868
f.addFuncAttribute(
4785-
g.lc.createEnumAttribute(attrAllockind, AllocFnKindAlloc or AllocFnKindZeroed)
4869+
g.lc.createEnumAttribute(
4870+
attrAllockind, AllocFnKindAlloc or AllocFnKindAligned or attrs[0]
4871+
)
47864872
)
4787-
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, cast[uint32](-1)))
4873+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllocsize, attrs[1]))
47884874
f.addAttributeAtIndex(
4789-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4875+
AttributeIndex(attrs[2] + 1), g.lc.createEnumAttribute(attrAllocalign, 0)
47904876
)
47914877
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
47924878
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
4793-
elif sym.name.s in ["alloc0", "newObj", "newObjRC1", "rawAlloc0", "allocShared0"]:
4879+
elif sym.name.s in ["dealloc", "deallocShared"]:
4880+
f.addFuncAttribute(g.attrNoUnwind)
47944881
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-
)
4882+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllockind, AllocFnKindFree))
4883+
48014884
f.addAttributeAtIndex(
4802-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4885+
AttributeIndex(typ.len - 1), g.lc.createEnumAttribute(attrAllocptr, 0)
48034886
)
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"]:
4887+
elif sym.name.s in ["rawDealloc"]:
4888+
f.addFuncAttribute(g.attrNoUnwind)
48084889
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-
)
4890+
f.addFuncAttribute(g.lc.createEnumAttribute(attrAllockind, AllocFnKindFree))
48174891
f.addAttributeAtIndex(
4818-
AttributeIndex(AttributeReturnIndex), g.lc.createEnumAttribute(attrAlign, 16)
4892+
AttributeIndex(2), g.lc.createEnumAttribute(attrAllocptr, 0)
48194893
)
4820-
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNonnull)
4821-
f.addAttributeAtIndex(AttributeIndex(AttributeReturnIndex), g.attrNoalias)
48224894

48234895
f
48244896

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

53895461
proc buildLoadVar(g: LLGen, typ: PType, v: llvm.ValueRef): llvm.ValueRef =
53905462
if typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
5391-
g.b.buildLoad2(g.ptrTy, v)
5463+
g.b.buildLoad2(g.llType(last(typ.skipTypes(abstractInst))), v)
53925464
else:
53935465
v
53945466

@@ -5427,14 +5499,12 @@ proc genOpenArrayConv(
54275499
else:
54285500
axp.v
54295501
v = g.buildLoadVar(n.typ, ax)
5430-
54315502
(g.getNimSeqDataPtr(seqTy, v), g.loadNimSeqLen(v))
54325503
of tyOpenArray, tyVarargs:
5433-
let ax = g.buildLoadVar(n.typ, g.genNode(n, false).v)
5434-
5504+
let ax = g.buildLoadVar(n.typ, g.genNode(n, true).v)
54355505
(
5436-
g.b.buildLoad2(g.ptrTy, g.buildOpenArrayDataGEP(ax)),
5437-
g.b.buildLoad2(g.intTy, g.buildOpenArrayLenGEP(ax)),
5506+
g.b.buildExtractValue(ax, 0, g.nn("p", ax)),
5507+
g.b.buildExtractValue(ax, 1, g.nn("l", ax)),
54385508
)
54395509
of tyArray, tyUncheckedArray:
54405510
var v = g.genNode(n, true).v
@@ -5798,9 +5868,8 @@ proc genAssignment(g: LLGen, dest, src: LLValue, typ: PType, flags: TAssignmentF
57985868
of tyString:
57995869
if optSeqDestructors in g.config.globalOptions:
58005870
discard g.b.buildStore(src.v, dest.v)
5801-
elif ({needToCopy} * flags == {} and src.storage != OnStatic) or canMove(
5802-
g, src.lode
5803-
):
5871+
elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or
5872+
canMove(g, src.lode):
58045873
g.genRefAssign(dest, src.v)
58055874
else:
58065875
if (dest.storage == OnStack and g.config.selectedGC != gcGo) or
@@ -5961,9 +6030,15 @@ proc genConstBracket(g: LLGen, n: PNode): llvm.ValueRef =
59616030
for i, s in n.sons:
59626031
vals[i] = g.genConstInitializer(s)
59636032
let s = constArray(et, vals)
5964-
if typ.kind in {tyArray, tyUncheckedArray}:
6033+
case typ.kind
6034+
of tyArray, tyUncheckedArray:
59656035
s
5966-
else:
6036+
of tyOpenArray:
6037+
let lit = g.m.addPrivateConstant(s.typeOfX, g.nn(".oa", n))
6038+
lit.setInitializer(s)
6039+
6040+
llvm.constNamedStruct(g.llOpenArrayType(), [lit, g.constInt64(vals.len)])
6041+
of tySequence:
59676042
let
59686043
ll = g.constNimInt(vals.len)
59696044
cap = g.constNimInt(vals.len + g.strLitFlag)
@@ -5980,6 +6055,8 @@ proc genConstBracket(g: LLGen, n: PNode): llvm.ValueRef =
59806055
lit = g.m.addPrivateConstant(payload.typeOfX, g.nn(".seq", n))
59816056
lit.setInitializer(payload)
59826057
lit
6058+
else:
6059+
raiseAssert "Unexpected const bracket: " & $typ.kind
59836060

59846061
proc genConstObjConstr(g: LLGen, n: PNode): llvm.ValueRef =
59856062
let
@@ -6748,6 +6825,8 @@ proc genMagicLengthStr(g: LLGen, n: PNode): LLValue =
67486825
fty = llvm.functionType(g.csizetTy, [g.primitives[tyCString]])
67496826
f = g.m.getOrInsertFunction("strlen", fty)
67506827

6828+
f.addFuncAttribute(g.attrNoUnwind)
6829+
67516830
let v1 = g.buildTruncOrExt(
67526831
g.b.buildCall2(fty, f, [v], g.nn("str.len.call", n)), g.primitives[tyInt], false
67536832
)
@@ -7790,13 +7869,49 @@ proc genMagicMove(g: LLGen, n: PNode, load: bool): LLValue =
77907869

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

78027917
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)