diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2023-10-31 21:32:09 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-31 21:32:09 +0100 |
commit | 801c02bf48b1612c8bf58ed113f944c9d6e32ead (patch) | |
tree | 750b978864d2d47b2dc22d38ee3feacb15871ba6 | |
parent | 2ae344f1c204f92a944afeb70fe6b1ca1c419f62 (diff) | |
download | Nim-801c02bf48b1612c8bf58ed113f944c9d6e32ead.tar.gz |
so close... (#22885)
-rw-r--r-- | compiler/main.nim | 4 | ||||
-rw-r--r-- | compiler/modulegraphs.nim | 1 | ||||
-rw-r--r-- | compiler/nir/ast2ir.nim | 94 | ||||
-rw-r--r-- | compiler/nir/nir.nim | 1 | ||||
-rw-r--r-- | compiler/nir/nirinsts.nim | 28 | ||||
-rw-r--r-- | compiler/nir/nirvm.nim | 283 | ||||
-rw-r--r-- | compiler/pipelines.nim | 3 |
7 files changed, 255 insertions, 159 deletions
diff --git a/compiler/main.nim b/compiler/main.nim index 0627a61bb..6651128eb 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -201,7 +201,9 @@ proc commandCompileToJS(graph: ModuleGraph) = proc commandInteractive(graph: ModuleGraph; useNir: bool) = graph.config.setErrorMaxHighMaybe initDefines(graph.config.symbols) - if not useNir: + if useNir: + defineSymbol(graph.config.symbols, "noSignalHandler") + else: defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 325c0adb1..ba636eb5a 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -92,6 +92,7 @@ type importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies suggestMode*: bool # whether we are in nimsuggest mode or not. invalidTransitiveClosure: bool + interactive*: bool inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the # first module that included it importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index f3b68474b..ad04dc103 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -205,7 +205,7 @@ proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId = c.code.copyTree Tree(v) build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, jk == opcTJmp) + c.code.boolVal(c.lit.numbers, info, jk == opcTJmp) c.code.gotoLabel info, Goto, result proc patch(c: var ProcCon; n: PNode; L: LabelId) = @@ -311,8 +311,7 @@ proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) = d = tmp else: let info = toLineInfo(c, n.info) - build c.code, info, Asgn: - c.code.addTyped info, typeToIr(c.m, n.typ) + buildTyped c.code, info, Asgn, typeToIr(c.m, n.typ): c.code.copyTree d c.code.copyTree tmp freeTemp(c, tmp) @@ -362,7 +361,7 @@ template buildCond(useNegation: bool; cond: typed; body: untyped) = c.code.copyTree cond build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, useNegation) + c.code.boolVal(c.lit.numbers, info, useNegation) c.code.gotoLabel info, Goto, lab body @@ -381,7 +380,7 @@ template buildIfThenElse(cond: typed; then, otherwise: untyped) = c.code.copyTree cond build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, false) + c.code.boolVal(c.lit.numbers, info, false) c.code.gotoLabel info, Goto, lelse then() @@ -406,8 +405,7 @@ proc genCase(c: var ProcCon; n: PNode; d: var Value) = let ending = newLabel(c.labelGen) let info = toLineInfo(c, n.info) withTemp(tmp, n[0]): - build c.code, info, Select: - c.code.addTyped info, typeToIr(c.m, n[0].typ) + buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ): c.gen(n[0], tmp) for i in 1..<n.len: let section = newLabel(c.labelGen) @@ -432,8 +430,7 @@ proc genCase(c: var ProcCon; n: PNode; d: var Value) = c.code.addLabel info, Label, ending proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) = - build c.code, info, opc: - c.code.addTyped info, t + buildTyped c.code, info, opc, t: if opc in {CheckedCall, CheckedIndirectCall}: c.code.addLabel info, CheckedGoto, c.exitLabel for a in mitems(args): @@ -479,8 +476,7 @@ proc genCall(c: var ProcCon; n: PNode; d: var Value) = if not isEmptyType(n.typ): if isEmpty(d): d = getTemp(c, n) # XXX Handle problematic aliasing here: `a = f_canRaise(a)`. - build c.code, info, Asgn: - c.code.addTyped info, tb + buildTyped c.code, info, Asgn, tb: c.code.copyTree d rawCall c, info, opc, tb, args else: @@ -492,8 +488,7 @@ proc genRaise(c: var ProcCon; n: PNode) = let tb = typeToIr(c.m, n[0].typ) let d = genx(c, n[0]) - build c.code, info, SetExc: - c.code.addTyped info, tb + buildTyped c.code, info, SetExc, tb: c.code.copyTree d c.freeTemp(d) c.code.addLabel info, Goto, c.exitLabel @@ -583,10 +578,10 @@ proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = if optBoundsCheck in c.options: let idx = move d build d, info, CheckedIndex: + d.Tree.addLabel info, CheckedGoto, c.exitLabel copyTree d.Tree, idx let x = toInt64 lengthOrd(c.config, arr) d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x - d.Tree.addLabel info, CheckedGoto, c.exitLabel proc rawGenNew(c: var ProcCon; d: Value; refType: PType; ninfo: TLineInfo; needsInit: bool) = assert refType.kind == tyRef @@ -715,7 +710,7 @@ proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = let t = typeToIr(c.m, n.typ) template body(target) = buildTyped target, info, opc, t: - if optOverflowCheck in c.options and opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}: + if opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}: target.addLabel info, CheckedGoto, c.exitLabel copyTree target, tmp copyTree target, tmp2 @@ -756,6 +751,8 @@ proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) = buildTyped c.code, info, Asgn, t: copyTree c.code, d buildTyped c.code, info, opc, t: + if opc in {CheckedAdd, CheckedSub}: + c.code.addLabel info, CheckedGoto, c.exitLabel copyTree c.code, d copyTree c.code, tmp c.freeTemp(tmp) @@ -974,7 +971,7 @@ proc genInSet(c: var ProcCon; n: PNode; d: var Value) = if ex == nil: let info = toLineInfo(c, n.info) template body(target) = - boolVal target, info, false + boolVal target, c.lit.numbers, info, false intoDest d, info, Bool8Id, body else: gen c, ex, d @@ -1062,7 +1059,7 @@ proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (Sy c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, false) + c.code.boolVal(c.lit.numbers, info, false) c.code.gotoLabel info, Goto, result[2] proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) = @@ -1080,7 +1077,7 @@ proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): ( copyTree c.code, last build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, false) + c.code.boolVal(c.lit.numbers, info, false) c.code.gotoLabel info, Goto, result[2] proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) = @@ -1126,7 +1123,7 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = c.code.copyTree d build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, false) + c.code.boolVal(c.lit.numbers, info, false) c.code.gotoLabel info, Goto, endLabel endLoop(c, info, idx, backLabel, endLabel) @@ -1462,6 +1459,7 @@ proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = buildTyped c.code, info, Asgn, c.m.nativeIntId: c.code.addSymUse info, tmpLen buildTyped c.code, info, CheckedAdd, c.m.nativeIntId: + c.code.addLabel info, CheckedGoto, c.exitLabel c.code.addSymUse info, tmpLen buildTyped c.code, info, FieldAt, typeToIr(c.m, n.typ): copyTree c.code, a @@ -1539,7 +1537,7 @@ proc genMove(c: var ProcCon; n: PNode; d: var Value) = build c.code, info, SelectPair: build c.code, info, SelectValue: - c.code.boolVal(info, true) + c.code.boolVal(c.lit.numbers, info, true) c.code.gotoLabel info, Goto, lab1 gen(c, n[3]) @@ -1636,6 +1634,7 @@ proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PTyp result = default(Value) let idx = genx(c, n) build result, info, CheckedIndex: + result.Tree.addLabel info, CheckedGoto, c.exitLabel copyTree result.Tree, idx case kind of ForSeq, ForStr: @@ -1649,7 +1648,6 @@ proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PTyp of ForArray: let x = toInt64 lengthOrd(c.config, arr) result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x - result.Tree.addLabel info, CheckedGoto, c.exitLabel freeTemp c, idx else: result = genx(c, n) @@ -1669,7 +1667,7 @@ proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo; buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): target.addImmediateVal info, 0 buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, pay[1]: + buildTyped target, info, DerefArrayAt, pay[1]: buildTyped target, info, FieldAt, typeToIr(c.m, arrType): copyTree target, x target.addImmediateVal info, 1 # (len, p)-pair @@ -1716,7 +1714,7 @@ proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo; buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): target.addImmediateVal info, 0 buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, pay: + buildTyped target, info, DerefArrayAt, pay: buildTyped target, info, FieldAt, typeToIr(c.m, arrType): copyTree target, x target.addImmediateVal info, 0 # (p, len)-pair @@ -1751,14 +1749,14 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, d) of mOr: c.genAndOr(n, opcTJmp, d) - of mPred, mSubI: c.genBinaryOp(n, d, CheckedSub) - of mSucc, mAddI: c.genBinaryOp(n, d, CheckedAdd) + of mPred, mSubI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedSub else: Sub) + of mSucc, mAddI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedAdd else: Add) of mInc: unused(c, n, d) - c.genIncDec(n, CheckedAdd) + c.genIncDec(n, if optOverflowCheck in c.options: CheckedAdd else: Add) of mDec: unused(c, n, d) - c.genIncDec(n, CheckedSub) + c.genIncDec(n, if optOverflowCheck in c.options: CheckedSub else: Sub) of mOrd, mChr, mUnown: c.gen(n[1], d) of generatedMagics: @@ -1773,9 +1771,9 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: genArrayLen(c, n, d) - of mMulI: genBinaryOp(c, n, d, CheckedMul) - of mDivI: genBinaryOp(c, n, d, CheckedDiv) - of mModI: genBinaryOp(c, n, d, CheckedMod) + of mMulI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMul else: Mul) + of mDivI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedDiv else: Div) + of mModI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMod else: Mod) of mAddF64: genBinaryOp(c, n, d, Add) of mSubF64: genBinaryOp(c, n, d, Sub) of mMulF64: genBinaryOp(c, n, d, Mul) @@ -1977,7 +1975,7 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; let t = typeToIr(c.m, typ) target.addImmediateVal info, 0 buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, c.m.strPayloadId[1]: + buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]: buildTyped target, info, FieldAt, typeToIr(c.m, arrType): copyTree target, tmp target.addImmediateVal info, 1 # (len, p)-pair @@ -1992,7 +1990,7 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; let t = typeToIr(c.m, typ) target.addImmediateVal info, 0 buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]: + buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]: buildTyped target, info, FieldAt, typeToIr(c.m, arrType): copyTree target, tmp target.addImmediateVal info, 1 # (len, p)-pair @@ -2102,10 +2100,11 @@ proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) = for i in 0..<n.len: var dd = default(Value) - buildTyped dd, info, ArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]: + buildTyped dd, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]: buildTyped dd, info, FieldAt, typeToIr(c.m, seqtype): copyTree Tree(dd), d - dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i + dd.addImmediateVal info, 1 # (len, p)-pair + dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i gen(c, n[i], dd) freeTemp c, d @@ -2242,10 +2241,10 @@ proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = let b = c.genx n[2] template body(target) = buildTyped target, info, CheckedRange, typeToIr(c.m, n.typ): + target.addLabel info, CheckedGoto, c.exitLabel copyTree target, tmp copyTree target, a copyTree target, b - target.addLabel info, CheckedGoto, c.exitLabel valueIntoDest c, info, d, n.typ, body freeTemp c, tmp freeTemp c, a @@ -2263,7 +2262,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = let b = genIndexCheck(c, n[1], a, ForStr, arrayType) let t = typeToIr(c.m, n.typ) template body(target) = - buildTyped target, info, ArrayAt, c.m.strPayloadId[1]: + buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]: buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): copyTree target, a target.addImmediateVal info, 1 # (len, p)-pair @@ -2276,7 +2275,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = let a = genx(c, n[0], flags) let b = genx(c, n[1]) template body(target) = - buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType): + buildTyped target, info, DerefArrayAt, typeToIr(c.m, arrayType): copyTree target, a copyTree target, b valueIntoDest c, info, d, n.typ, body @@ -2298,7 +2297,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = let b = genIndexCheck(c, n[1], a, ForOpenArray, arrayType) let t = typeToIr(c.m, n.typ) template body(target) = - buildTyped target, info, ArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ): + buildTyped target, info, DerefArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ): buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): copyTree target, a target.addImmediateVal info, 0 # (p, len)-pair @@ -2324,7 +2323,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = let b = genIndexCheck(c, n[1], a, ForSeq, arrayType) let t = typeToIr(c.m, n.typ) template body(target) = - buildTyped target, info, ArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]: + buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]: buildTyped target, info, FieldAt, t: copyTree target, a target.addImmediateVal info, 1 # (len, p)-pair @@ -2337,10 +2336,18 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = let info = toLineInfo(c, n.info) - let a = genx(c, n[0], flags) + + var n0 = n[0] + var opc = FieldAt + if n0.kind == nkDotExpr: + # obj[].a --> DerefFieldAt instead of FieldAt: + n0 = n[0] + opc = DerefFieldAt + + let a = genx(c, n0, flags) template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, n[0].typ): + buildTyped target, info, opc, typeToIr(c.m, n0.typ): copyTree target, a genField c, n[1], Value(target) @@ -2353,6 +2360,11 @@ proc genParams(c: var ProcCon; params: PNode; prc: PSym) = let res = resNode.sym # get result symbol c.code.addSummon toLineInfo(c, res.info), toSymId(c, res), typeToIr(c.m, res.typ), SummonResult + elif prc.typ.len > 0 and not isEmptyType(prc.typ[0]) and not isCompileTimeOnly(prc.typ[0]): + # happens for procs without bodies: + let t = typeToIr(c.m, prc.typ[0]) + let tmp = allocTemp(c, t) + c.code.addSummon toLineInfo(c, params.info), tmp, t, SummonResult for i in 1..<params.len: let s = params[i].sym diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim index 1efa6719a..6f7077fb0 100644 --- a/compiler/nir/nir.nim +++ b/compiler/nir/nir.nim @@ -57,6 +57,7 @@ proc evalStmt(c: PCtx; n: PNode) = #res.add "\n--------------------------\n" #toString res, c.m.types.g if pc.int < c.m.nirm.code.len: + c.bytecode.interactive = c.m.graph.interactive execCode c.bytecode, c.m.nirm.code, pc #echo res diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 873004408..5c2901540 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -63,8 +63,10 @@ type Kill, # `Kill x`: scope end for `x` AddrOf, - ArrayAt, # addr(a[i]) - FieldAt, # addr(obj.field) + ArrayAt, # a[i] + DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]` + FieldAt, # obj.field + DerefFieldAt, # obj[].field Load, # a[] Store, # a[] = b @@ -162,7 +164,9 @@ const AddrOf, Load, ArrayAt, + DerefArrayAt, FieldAt, + DerefFieldAt, TestOf } @@ -240,6 +244,8 @@ proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos) template firstSon*(n: NodePos): NodePos = NodePos(n.int+1) +template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2) + iterator sons*(tree: Tree; n: NodePos): NodePos = var pos = n.int assert tree.nodes[pos].kind > LastAtomicValue @@ -259,6 +265,17 @@ iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos = yield NodePos pos nextChild tree, pos +iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + for i in 1..toSkip: + nextChild tree, pos + while pos < last: + yield NodePos pos + nextChild tree, pos + template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int] proc span(tree: Tree; pos: int): int {.inline.} = @@ -327,9 +344,6 @@ proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcod t.nodes.add Instr(x: toX(k, uint32(result)), info: info) inc labelGen -proc boolVal*(t: var Tree; info: PackedLineInfo; b: bool) = - t.nodes.add Instr(x: toX(ImmediateVal, uint32(b)), info: info) - proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) = assert k in {Goto, GotoLoop, CheckedGoto} t.nodes.add Instr(x: toX(k, uint32(L)), info: info) @@ -367,6 +381,10 @@ proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; buildTyped t, info, NumberConv, typ: t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info) +proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) = + buildTyped t, info, NumberConv, Bool8Id: + t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info) + proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) = t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info) diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim index 4b2c7e548..a3d69a296 100644 --- a/compiler/nir/nirvm.nim +++ b/compiler/nir/nirvm.nim @@ -48,7 +48,9 @@ type AddrOfM, ArrayAtM, # (elemSize, addr(a), i) + DerefArrayAtM, FieldAtM, # addr(obj.field) + DerefFieldAtM, LoadM, # a[] AsgnM, # a = b @@ -60,7 +62,6 @@ type CheckedIndexM, CallM, - CheckedCallM, # call that can raise CheckedAddM, # with overflow checking etc. CheckedSubM, CheckedMulM, @@ -107,6 +108,14 @@ template toIns(k: OpcodeM; operand: uint32): Instr = template toIns(k: OpcodeM; operand: LitId): Instr = Instr(uint32(k) or (operand.uint32 shl OpcodeBits)) +type + NimStrPayloadVM = object + cap: int + data: UncheckedArray[char] + NimStringVM = object + len: int + p: ptr NimStrPayloadVM + const GlobalsSize = 1024*24 @@ -119,7 +128,8 @@ type debug: seq[PackedLineInfo] m: ref NirModule procs: Table[SymId, CodePos] - globals: Table[SymId, uint32] + globals: Table[SymId, (uint32, int)] + strings: Table[LitId, NimStringVM] globalData: pointer globalsAddr: uint32 typeImpls: Table[string, TypeId] @@ -127,6 +137,7 @@ type sizes: Table[TypeId, (int, int)] # (size, alignment) oldTypeLen: int procUsagesToPatch: Table[SymId, seq[CodePos]] + interactive*: bool Universe* = object ## all units: For interpretation we need that units: seq[Bytecode] @@ -325,7 +336,7 @@ proc toString*(t: Bytecode; pos: CodePos; r.add $t.m.lit.numbers[LitId t[pos].operand] of StrValM: escapeToNimLit(t.m.lit.strings[LitId t[pos].operand], r) - of LoadLocalM, LoadGlobalM, LoadProcM, AllocLocals: + of LoadLocalM, LoadGlobalM, LoadProcM, AllocLocals, SummonParamM: r.add $t[pos].kind r.add ' ' r.add $t[pos].operand @@ -361,7 +372,7 @@ type u: ref Universe known: Table[LabelId, CodePos] toPatch: Table[LabelId, seq[CodePos]] - locals: Table[SymId, uint32] + locals: Table[SymId, (uint32, int)] # address, size thisModule: uint32 localsAddr: uint32 markedWithLabel: IntSet @@ -382,15 +393,23 @@ type AddrMode = enum InDotExpr, WantAddr -template maybeDeref(doDeref: bool; body: untyped) = +template maybeDeref(doDeref: bool; size: int; body: untyped) = var pos = PatchPos(-1) if doDeref: pos = prepare(bc, info, LoadM) - bc.add info, TypedM, 0'u32 + bc.add info, ImmediateValM, uint32 size body if doDeref: patch(bc, pos) +proc toReadonlyString(s: string): NimStringVM = + if s.len == 0: + result = NimStringVM(len: 0, p: nil) + else: + result = NimStringVM(len: s.len, p: cast[ptr NimStrPayloadVM](alloc(s.len+1+sizeof(int)))) + copyMem(addr result.p.data[0], addr s[0], s.len+1) + result.p.cap = s.len or (1 shl (8 * 8 - 2)) # see also NIM_STRLIT_FLAG + const ForwardedProc = 10_000_000'u32 @@ -409,19 +428,24 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla of IntVal: bc.add info, IntValM, t[n].rawOperand of StrVal: + let litId = LitId t[n].rawOperand + if not bc.strings.hasKey(litId): + bc.strings[litId] = toReadonlyString(bc.m.lit.strings[litId]) bc.add info, StrValM, t[n].rawOperand of SymDef: discard "happens for proc decls. Don't copy the node as we don't need it" of SymUse: let s = t[n].symId if c.locals.hasKey(s): - maybeDeref(WantAddr notin flags): - bc.add info, LoadLocalM, c.locals[s] + let (address, size) = c.locals[s] + maybeDeref(WantAddr notin flags, size): + bc.add info, LoadLocalM, address elif bc.procs.hasKey(s): bc.add info, LoadProcM, uint32 bc.procs[s] elif bc.globals.hasKey(s): - maybeDeref(WantAddr notin flags): - bc.add info, LoadGlobalM, uint32 s + let (address, size) = bc.globals[s] + maybeDeref(WantAddr notin flags, size): + bc.add info, LoadGlobalM, address else: let here = CodePos(bc.code.len) bc.add info, LoadProcM, ForwardedProc + uint32(s) @@ -452,7 +476,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla bc.add info, NilValM, t[n].rawOperand of LoopLabel, Label: let lab = t[n].label - let here = CodePos(bc.code.len-1) + let here = CodePos(bc.code.len) c.known[lab] = here var p: seq[CodePos] = @[] if c.toPatch.take(lab, p): @@ -506,7 +530,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla let (size, alignment) = computeSize(bc, tid) let global = align(bc.globalsAddr, uint32 alignment) - bc.globals[s] = global + bc.globals[s] = (global, size) bc.globalsAddr += uint32 size assert bc.globalsAddr < GlobalsSize @@ -518,7 +542,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla let (size, alignment) = computeSize(bc, tid) let local = align(c.localsAddr, uint32 alignment) - c.locals[s] = local + c.locals[s] = (local, size) c.localsAddr += uint32 size # allocation is combined into the frame allocation so there is no # instruction to emit @@ -530,7 +554,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla let (size, alignment) = computeSize(bc, tid) let local = align(c.localsAddr, uint32 alignment) - c.locals[s] = local + c.locals[s] = (local, size) c.localsAddr += uint32 size bc.add info, SummonParamM, local bc.add info, ImmediateValM, uint32 size @@ -545,39 +569,30 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla let (arrayType, a, i) = sons3(t, n) let tid = t[arrayType].typeId let size = uint32 computeElemSize(bc, tid) - if t[a].kind == Load: - let (_, arg) = sons2(t, a) - build bc, info, LoadM: - bc.add info, ImmediateValM, size - build bc, info, ArrayAtM: - bc.add info, ImmediateValM, size - preprocess(c, bc, t, arg, {WantAddr}) - preprocess(c, bc, t, i, {WantAddr}) - else: - build bc, info, ArrayAtM: - bc.add info, ImmediateValM, size - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, i, {WantAddr}) + build bc, info, ArrayAtM: + bc.add info, ImmediateValM, size + preprocess(c, bc, t, a, {WantAddr}) + preprocess(c, bc, t, i, {WantAddr}) + of DerefArrayAt: + let (arrayType, a, i) = sons3(t, n) + let tid = t[arrayType].typeId + let size = uint32 computeElemSize(bc, tid) + build bc, info, DerefArrayAtM: + bc.add info, ImmediateValM, size + preprocess(c, bc, t, a, {WantAddr}) + preprocess(c, bc, t, i, {WantAddr}) of FieldAt: - # a[] conceptually loads a block of size of T. But when applied to an object selector - # only a subset of the data is really requested so `(a[] : T).field` - # becomes `(a+offset(field))[] : T_Field` - # And now if this is paired with `addr` the deref disappears, as usual: `addr x.field[]` - # is `(x+offset(field))`. let (typ, a, b) = sons3(t, n) - if t[a].kind == Load: - let (_, arg) = sons2(t, a) - build bc, info, LoadM: - bc.add info, ImmediateValM, uint32 computeSize(bc, t[typ].typeId)[0] - let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] - build bc, info, FieldAtM: - preprocess(c, bc, t, arg, flags+{WantAddr}) - bc.add info, ImmediateValM, uint32(offset) - else: - let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] - build bc, info, FieldAtM: - preprocess(c, bc, t, a, flags+{WantAddr}) - bc.add info, ImmediateValM, uint32(offset) + let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] + build bc, info, FieldAtM: + preprocess(c, bc, t, a, flags+{WantAddr}) + bc.add info, ImmediateValM, uint32(offset) + of DerefFieldAt: + let (typ, a, b) = sons3(t, n) + let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] + build bc, info, DerefFieldAtM: + preprocess(c, bc, t, a, flags+{WantAddr}) + bc.add info, ImmediateValM, uint32(offset) of Load: let (elemType, a) = sons2(t, n) let tid = t[elemType].typeId @@ -593,14 +608,16 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla if t[src].kind in {Call, IndirectCall}: # No support for return values, these are mapped to `var T` parameters! build bc, info, CallM: - preprocess(c, bc, t, src.firstSon, {WantAddr}) + preprocess(c, bc, t, src.skipTyped, {WantAddr}) preprocess(c, bc, t, dest, {WantAddr}) - for ch in sonsFrom1(t, src): preprocess(c, bc, t, ch, {WantAddr}) + for ch in sonsFromN(t, src, 2): preprocess(c, bc, t, ch, {WantAddr}) elif t[src].kind in {CheckedCall, CheckedIndirectCall}: - build bc, info, CheckedCallM: - preprocess(c, bc, t, src.firstSon, {WantAddr}) + let (_, gotoInstr, fn) = sons3(t, src) + build bc, info, CallM: + preprocess(c, bc, t, fn, {WantAddr}) preprocess(c, bc, t, dest, {WantAddr}) - for ch in sonsFrom1(t, src): preprocess(c, bc, t, ch, {WantAddr}) + for ch in sonsFromN(t, src, 3): preprocess(c, bc, t, ch, {WantAddr}) + preprocess c, bc, t, gotoInstr, {} elif t[dest].kind == Load: let (typ, a) = sons2(t, dest) let s = computeSize(bc, tid)[0] @@ -628,8 +645,11 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr}) of CheckedCall, CheckedIndirectCall: # avoid the Typed thing at position 0: - build bc, info, CheckedCallM: - for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr}) + let (_, gotoInstr, fn) = sons3(t, n) + build bc, info, CallM: + preprocess(c, bc, t, fn, {WantAddr}) + for ch in sonsFromN(t, n, 3): preprocess(c, bc, t, ch, {WantAddr}) + preprocess c, bc, t, gotoInstr, {WantAddr} of CheckedAdd: recurse CheckedAddM of CheckedSub: @@ -696,7 +716,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla for ch in sons(t, n): preprocess(c2, bc, t, ch, {}) bc.code[toPatch] = toIns(AllocLocals, c2.localsAddr) when false: - if here.int == 40192: + if here.int == 39850: debug bc, t, n debug bc, here @@ -776,6 +796,10 @@ proc evalAddr(c: Bytecode; pc: CodePos; s: StackFrame): pointer = let (x, offset) = sons2(c.code, pc) result = evalAddr(c, x, s) result = result +! c.code[offset].operand + of DerefFieldAtM: + let (x, offset) = sons2(c.code, pc) + let p = evalAddr(c, x, s) + result = cast[ptr pointer](p)[] +! c.code[offset].operand of ArrayAtM: let (e, a, i) = sons3(c.code, pc) let elemSize = c.code[e].operand @@ -783,10 +807,13 @@ proc evalAddr(c: Bytecode; pc: CodePos; s: StackFrame): pointer = var idx: int = 0 eval(c, i, s, addr idx, sizeof(int)) result = result +! (uint32(idx) * elemSize) - of LoadM: - let (_, arg) = sons2(c.code, pc) - let p = evalAddr(c, arg, s) - result = cast[ptr pointer](p)[] + of DerefArrayAtM: + let (e, a, i) = sons3(c.code, pc) + let elemSize = c.code[e].operand + var p = evalAddr(c, a, s) + var idx: int = 0 + eval(c, i, s, addr idx, sizeof(int)) + result = cast[ptr pointer](p)[] +! (uint32(idx) * elemSize) of LoadGlobalM: result = c.globalData +! c.code[pc].operand else: @@ -897,23 +924,29 @@ proc evalSelect(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = for pair in sonsFrom2(c, pc): assert c.code[pair].kind == SelectPairM let (values, action) = sons2(c.code, pair) - assert c.code[values].kind == SelectListM - for v in sons(c, values): - case c.code[v].kind - of SelectValueM: - var a = default(typ) - eval c, v.firstSon, s, addr a, sizeof(typ) - if selector == a: - return CodePos c.code[action].operand - of SelectRangeM: - let (va, vb) = sons2(c.code, v) - var a = default(typ) - eval c, va, s, addr a, sizeof(typ) - var b = default(typ) - eval c, vb, s, addr a, sizeof(typ) - if a <= selector and selector <= b: - return CodePos c.code[action].operand - else: raiseAssert "unreachable" + if c.code[values].kind == SelectValueM: + var a = default(typ) + eval c, values.firstSon, s, addr a, sizeof(typ) + if selector == a: + return CodePos c.code[action].operand + else: + assert c.code[values].kind == SelectListM, $c.code[values].kind + for v in sons(c, values): + case c.code[v].kind + of SelectValueM: + var a = default(typ) + eval c, v.firstSon, s, addr a, sizeof(typ) + if selector == a: + return CodePos c.code[action].operand + of SelectRangeM: + let (va, vb) = sons2(c.code, v) + var a = default(typ) + eval c, va, s, addr a, sizeof(typ) + var b = default(typ) + eval c, vb, s, addr a, sizeof(typ) + if a <= selector and selector <= b: + return CodePos c.code[action].operand + else: raiseAssert "unreachable" result = CodePos(-1) let (t, sel) = sons2(c.code, pc) @@ -932,11 +965,18 @@ proc evalSelect(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) = case c.code[pc].kind of LoadLocalM: - let dest = s.locals +! c.code[pc].operand - copyMem dest, result, size - of FieldAtM, ArrayAtM, LoadM: - let dest = evalAddr(c, pc, s) - copyMem dest, result, size + let src = s.locals +! c.code[pc].operand + copyMem result, src, size + of FieldAtM, DerefFieldAtM, ArrayAtM, DerefArrayAtM, LoadGlobalM: + let src = evalAddr(c, pc, s) + copyMem result, src, size + of LoadProcM: + let procAddr = c.code[pc].operand + cast[ptr pointer](result)[] = cast[pointer](procAddr) + of LoadM: + let (_, arg) = sons2(c.code, pc) + let src = evalAddr(c, arg, s) + copyMem result, src, size of CheckedAddM: checkedBinop `+` of CheckedSubM: checkedBinop `-` of CheckedMulM: checkedBinop `*` @@ -958,8 +998,7 @@ proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) = of StrValM: # binary compatible and no deep copy required: - copyMem(cast[ptr string](result), addr(c.m.lit.strings[c[pc].litId]), sizeof(string)) - # XXX not correct! + copyMem(cast[ptr string](result), addr(c.strings[c[pc].litId]), sizeof(string)) of ObjConstrM: for offset, size, val in triples(c, pc): eval c, val, s, result+!offset, size @@ -1013,31 +1052,41 @@ proc evalProc(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = assert procSym < ForwardedProc result = CodePos(procSym) -proc echoImpl(c: Bytecode; pc: CodePos; s: StackFrame) = - type StringArray = object - len: int - data: ptr UncheckedArray[string] - var sa = default(StringArray) +proc echoImpl(c: Bytecode; pc: CodePos; frame: StackFrame) = + var s = default(NimStringVM) for a in sonsFrom1(c, pc): - eval(c, a, s, addr(sa), sizeof(sa)) - for i in 0..<sa.len: - stdout.write sa.data[i] + assert c[a].kind == ArrayConstrM + let elemSize = c.code[a.firstSon].operand.int + for ch in sonsFrom1(c, a): + eval c, ch, frame, addr s, elemSize + if s.len > 0: + discard stdout.writeBuffer(addr(s.p.data[0]), s.len) stdout.write "\n" stdout.flushFile() -proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; didEval: var bool): CodePos = +type + EvalBuiltinState = enum + DidNothing, DidEval, DidError + +proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; state: var EvalBuiltinState): CodePos = var prc = prc while true: case c[prc].kind of PragmaPairM: let (x, y) = sons2(c.code, prc) - if cast[PragmaKey](c[x]) == CoreName: + let key = cast[PragmaKey](c[x].operand) + case key + of CoreName: let lit = c[y].litId case c.m.lit.strings[lit] of "echoBinSafe": echoImpl(c, pc, s) - else: discard - echo "running compilerproc: ", c.m.lit.strings[lit] - didEval = true + else: + raiseAssert "cannot eval: " & c.m.lit.strings[lit] + state = DidEval + of HeaderImport, DllImport: + let lit = c[y].litId + raiseAssert "cannot eval: " & c.m.lit.strings[lit] + else: discard of PragmaIdM, AllocLocals: discard else: break next c, prc @@ -1045,52 +1094,63 @@ proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; didEval: proc exec(c: Bytecode; pc: CodePos; u: ref Universe) = var pc = pc - var s = StackFrame(u: u) + var frame = StackFrame(u: u) while pc.int < c.code.len: + when false: # c.interactive: + echo "running: ", pc.int + debug c, pc + case c.code[pc].kind of GotoM: pc = CodePos(c.code[pc].operand) of AsgnM: let (sz, a, b) = sons3(c.code, pc) - let dest = evalAddr(c, a, s) - eval(c, b, s, dest, c.code[sz].operand.int) + let dest = evalAddr(c, a, frame) + eval(c, b, frame, dest, c.code[sz].operand.int) next c, pc of StoreM: let (sz, a, b) = sons3(c.code, pc) - let destPtr = evalAddr(c, a, s) + let destPtr = evalAddr(c, a, frame) let dest = cast[ptr pointer](destPtr)[] - eval(c, b, s, dest, c.code[sz].operand.int) + eval(c, b, frame, dest, c.code[sz].operand.int) next c, pc of CallM: # No support for return values, these are mapped to `var T` parameters! - var prc = evalProc(c, pc.firstSon, s) + var prc = evalProc(c, pc.firstSon, frame) assert c.code[prc.firstSon].kind == AllocLocals let frameSize = int c.code[prc.firstSon].operand # skip stupid stuff: - var didEval = false - prc = evalBuiltin(c, pc, s, prc.firstSon, didEval) - if didEval: + var evalState = DidNothing + prc = evalBuiltin(c, pc, frame, prc.firstSon, evalState) + if evalState != DidNothing: next c, pc + if pc.int < c.code.len and c.code[pc].kind == CheckedGotoM: + if evalState == DidEval: + next c, pc + else: + pc = CodePos(c.code[pc].operand) else: # setup storage for the proc already: let callInstr = pc next c, pc - let s2 = newStackFrame(frameSize, s, pc) + let s2 = newStackFrame(frameSize, frame, pc) for a in sonsFrom1(c, callInstr): assert c[prc].kind == SummonParamM let paramAddr = c[prc].operand next c, prc assert c[prc].kind == ImmediateValM let paramSize = c[prc].operand.int - eval(c, a, s2, s2.locals +! paramAddr, paramSize) next c, prc - s = s2 + eval(c, a, s2, s2.locals +! paramAddr, paramSize) + frame = s2 pc = prc of RetM: - pc = s.returnAddr - s = popStackFrame(s) + pc = frame.returnAddr + if c.code[pc].kind == CheckedGotoM: + pc = frame.jumpTo + frame = popStackFrame(frame) of SelectM: - let pc2 = evalSelect(c, pc, s) + let pc2 = evalSelect(c, pc, frame) if pc2.int >= 0: pc = pc2 else: @@ -1107,8 +1167,9 @@ proc execCode*(bc: var Bytecode; t: Tree; n: NodePos) = let start = CodePos(bc.code.len) var pc = n while pc.int < t.len: - #echo "RUnning: " - #debug bc, t, pc + #if bc.interactive: + # echo "RUnning: " + # debug bc, t, pc preprocess c, bc, t, pc, {} next t, pc exec bc, start, nil diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index e4c484e1f..8f40ac031 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -148,9 +148,10 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator if s == nil: rawMessage(graph.config, errCannotOpenFile, filename.string) return false + graph.interactive = false else: s = stream - + graph.interactive = stream.kind == llsStdIn while true: syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config) |