diff options
author | Araq <rumpf_a@web.de> | 2014-07-08 02:02:58 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-07-08 02:02:58 +0200 |
commit | f16449ec22751ca0e864d70424760e540a80c804 (patch) | |
tree | a6dc81d9b0ae81d925345e64b4d32ec611ac8d22 | |
parent | f9d7e8db2a8b89575df51768ce9e5aa66c47cc9f (diff) | |
download | Nim-f16449ec22751ca0e864d70424760e540a80c804.tar.gz |
fixes #1103; fixes #1297
-rw-r--r-- | compiler/vm.nim | 17 | ||||
-rw-r--r-- | compiler/vmgen.nim | 82 | ||||
-rw-r--r-- | tests/casestmt/tcase_arrayconstr.nim | 19 | ||||
-rw-r--r-- | tests/macros/tbug1149.nim | 46 | ||||
-rw-r--r-- | todo.txt | 4 |
5 files changed, 137 insertions, 31 deletions
diff --git a/compiler/vm.nim b/compiler/vm.nim index 7f5e0d1c5..f879e36bc 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -438,7 +438,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rc].intVal > high(int): stackTrace(c, tos, pc, errIndexOutOfBounds) let idx = regs[rc].intVal.int - # XXX what if the array is not 0-based? -> codegen should insert a sub let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: regs[ra].node = src.sons[idx] @@ -504,13 +503,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: - # a[] = b + # a[] = c; b unused let ra = instr.regA - let rb = instr.regB + let rc = instr.regC case regs[ra].kind - of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rb]) - of rkRegisterAddr: regs[ra].regAddr[] = regs[rb] - of rkNode: putIntoNode(regs[ra].node, regs[rb]) + of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc]) + of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] + of rkNode: putIntoNode(regs[ra].node, regs[rc]) else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) @@ -751,11 +750,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal.add getstr(regs[i]) of opcAddStrCh: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].node.strVal) of opcAddSeqElem: decodeB(rkNode) @@ -992,7 +991,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = return TFullReg(kind: rkNone) of opcSetLenStr: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.setLen(regs[rb].intVal.int) of opcOf: decodeBC(rkInt) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 28e0a8fd6..b04e60549 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -19,7 +19,13 @@ # .. code-block:: nimrod # let s = a & b # no matter what, create fresh node # s = a & b # no matter what, keep the node -# +# +# Also *stores* into non-temporary memory need to perform deep copies: +# a.b = x.y +# We used to generate opcAsgn for the *load* of 'x.y' but this is clearly +# wrong! We need to produce opcAsgn (the copy) for the *store*. This also +# solves the opcLdConst vs opcAsgnConst issue. Of course whether we need +# this copy depends on the involved types. import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, @@ -531,12 +537,12 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) - c.gABC(le, opcWrDeref, dest, value) + c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) of nkSym: if le.sym.isGlobal: let dest = c.genx(le, {gfAddrOf}) - c.gABC(le, opcWrDeref, dest, value) + c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) else: discard @@ -1097,6 +1103,25 @@ proc checkCanEval(c: PCtx; n: PNode) = not s.isOwnedBy(c.prc.sym) and s.owner != c.module: cannotEval(n) +proc isTemp(c: PCtx; dest: TDest): bool = + result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown + +template needsAdditionalCopy(n): expr = + not c.isTemp(dest) and not fitsRegister(n.typ) + +proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; + dest, idx, value: TRegister) = + # opcLdObj et al really means "load address". We sometimes have to create a + # copy in order to not introduce false aliasing: + # mylocal = a.b # needs a copy of the data! + if needsAdditionalCopy(n): + var cc = c.getTemp(n.typ) + c.gABC(n, whichAsgnOpc(n), cc, value) + c.gABC(n, opc, dest, idx, cc) + c.freeTemp(cc) + else: + c.gABC(n, opc, dest, idx, value) + proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: @@ -1105,9 +1130,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: - c.gABC(le, opcWrStrIdx, dest, idx, tmp) + c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp) else: - c.gABC(le, opcWrArr, dest, idx, tmp) + c.preventFalseAlias(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here @@ -1115,12 +1140,12 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = genField(left.sons[1]) let tmp = c.genx(ri) - c.gABC(left, opcWrObj, dest, idx, tmp) + c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) let tmp = c.genx(ri) - c.gABC(le, opcWrDeref, dest, tmp) + c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp) c.freeTemp(tmp) of nkSym: let s = le.sym @@ -1129,24 +1154,32 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = withTemp(tmp, le.typ): c.gen(le, tmp, {gfAddrOf}) let val = c.genx(ri) - c.gABC(le, opcWrDeref, tmp, val) + c.preventFalseAlias(le, opcWrDeref, tmp, 0, val) c.freeTemp(val) else: if s.kind == skForVar: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) - gen(c, ri, dest) + if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}: + var cc = c.getTemp(le.typ) + gen(c, ri, cc) + c.gABC(le, whichAsgnOpc(le), dest, cc) + c.freeTemp(cc) + else: + gen(c, ri, dest) else: let dest = c.genx(le, {gfAddrOf}) genAsgn(c, dest, ri, requiresCopy) proc genLit(c: PCtx; n: PNode; dest: var TDest) = - var opc = opcLdConst + # opcLdConst is now always valid. We produce the necessary copy in the + # assignments now: + #var opc = opcLdConst if dest < 0: dest = c.getTemp(n.typ) - elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst + #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst let lit = genLiteral(c, n) - c.gABx(n, opc, dest, lit) + c.gABx(n, opcLdConst, dest, lit) proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = var n = newNode(nkType) @@ -1175,7 +1208,7 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) let tmp = c.genx(s.ast) - c.gABC(n, opcWrDeref, dest, tmp) + c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) @@ -1332,7 +1365,7 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind != nkEmpty: let tmp = c.genx(a.sons[0], {gfAddrOf}) let val = c.genx(a.sons[2]) - c.gABC(a, opcWrDeref, tmp, val) + c.preventFalseAlias(a, opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) else: @@ -1342,7 +1375,14 @@ proc genVarSection(c: PCtx; n: PNode) = else: if not fitsRegister(s.typ): c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) - gen(c, a.sons[2], s.position.TRegister) + let le = a.sons[0] + if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}: + var cc = c.getTemp(le.typ) + gen(c, a.sons[2], cc) + c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc) + c.freeTemp(cc) + else: + gen(c, a.sons[2], s.position.TRegister) else: # assign to a.sons[0]; happens for closures if a.sons[2].kind == nkEmpty: @@ -1370,7 +1410,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: let a = c.genx(x) - c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) + c.preventFalseAlias(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) @@ -1402,7 +1442,8 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym: let idx = genField(it.sons[0]) let tmp = c.genx(it.sons[1]) - c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) + c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + dest, idx, tmp) c.freeTemp(tmp) else: internalError(n.info, "invalid object constructor") @@ -1416,11 +1457,12 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if it.kind == nkExprColonExpr: let idx = genField(it.sons[0]) let tmp = c.genx(it.sons[1]) - c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) + c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + dest, idx, tmp) c.freeTemp(tmp) else: let tmp = c.genx(it) - c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) + c.preventFalseAlias(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) c.freeTemp(tmp) proc genProc*(c: PCtx; s: PSym): int @@ -1658,7 +1700,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "find": + #if s.name.s == "get_data": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/tests/casestmt/tcase_arrayconstr.nim b/tests/casestmt/tcase_arrayconstr.nim new file mode 100644 index 000000000..cd7156600 --- /dev/null +++ b/tests/casestmt/tcase_arrayconstr.nim @@ -0,0 +1,19 @@ +discard """ + output: '''Not found! +Found!''' +""" + +const + md_extension = [".md", ".markdown"] + +proc test(ext: string) = + case ext + of ".txt", md_extension: + echo "Found!" + else: + echo "Not found!" + +test(".something") +# ensure it's not evaluated at compile-time: +var foo = ".markdown" +test(foo) diff --git a/tests/macros/tbug1149.nim b/tests/macros/tbug1149.nim index 5c4cb8530..d2bff61d3 100644 --- a/tests/macros/tbug1149.nim +++ b/tests/macros/tbug1149.nim @@ -2,7 +2,13 @@ discard """ msg: '''a s d -f''' +f +TTaa +TTaa +TTaa +TTaa''' + +output: '''test''' """ type @@ -18,3 +24,41 @@ macro test(): stmt = echo i.s test() + + +# bug 1297 + +import macros + +type TType = tuple[s: string] + +macro echotest(): stmt = + var t: TType + t.s = "" + t.s.add("test") + result = newCall(newIdentNode("echo"), newStrLitNode(t.s)) + +echotest() + +# bug #1103 + +type + Td = tuple + a:string + b:int + +proc get_data(d: Td) : string {.compileTime.} = + result = d.a # Works if a literal string is used here. + # Bugs if line A or B is active. Works with C + result &= "aa" # A + #result.add("aa") # B + #result = result & "aa" # C + +macro m(s:static[Td]) : stmt = + echo get_data(s) + echo get_data(s) + result = newEmptyNode() + +const s=("TT", 3) +m(s) +m(s) diff --git a/todo.txt b/todo.txt index f027be8d5..8a1caee3b 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,8 @@ version 0.9.6 ============= -- some table related tests are wrong (memory usage checks) +- integrate the new LL into the devel branch +- start experimental branch Concurrency ----------- @@ -31,6 +32,7 @@ Misc - type API for macros; make 'spawn' a macro - markAndSweepGC should expose an API for fibers - prevent 'alloc(TypeWithGCedMemory)' +- some table related tests are wrong (memory usage checks) Bugs |