diff options
Diffstat (limited to 'compiler/ccgcalls.nim')
-rw-r--r-- | compiler/ccgcalls.nim | 395 |
1 files changed, 302 insertions, 93 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index b01a81c5e..2dacc25e9 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -19,13 +19,13 @@ proc hasNoInit(call: PNode): bool {.inline.} = result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, - callee, params: PRope) = - var pl = con(callee, ~"(", params) + callee, params: Rope) = + var pl = callee & ~"(" & params # getUniqueType() is too expensive here: var typ = skipTypes(ri.sons[0].typ, abstractInst) if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): - if params != nil: pl.app(~", ") + if params != nil: pl.add(~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): # Great, we can use 'd': @@ -33,26 +33,34 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: resetLoc(p, d) - app(pl, addrLoc(d)) - app(pl, ~");$n") + add(pl, addrLoc(d)) + add(pl, ~");$n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - app(pl, addrLoc(tmp)) - app(pl, ~");$n") + add(pl, addrLoc(tmp)) + add(pl, ~");$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: - app(pl, ~")") - if d.k == locNone: getTemp(p, typ.sons[0], d) - assert(d.t != nil) # generate an assignment to d: - var list: TLoc - initLoc(list, locCall, d.t, OnUnknown) - list.r = pl - genAssignment(p, d, list, {}) # no need for deep copying + add(pl, ~")") + if p.module.compileToCpp and lfSingleUse in d.flags: + # do not generate spurious temporaries for C++! For C we're better off + # with them to prevent undefined behaviour and because the codegen + # is free to emit expressions multiple times! + d.k = locCall + d.r = pl + excl d.flags, lfSingleUse + else: + if d.k == locNone: getTemp(p, typ.sons[0], d) + assert(d.t != nil) # generate an assignment to d: + var list: TLoc + initLoc(list, locCall, d.t, OnUnknown) + list.r = pl + genAssignment(p, d, list, {}) # no need for deep copying else: - app(pl, ~");$n") + add(pl, ~");$n") line(p, cpsStmts, pl) proc isInCurrentFrame(p: BProc, n: PNode): bool = @@ -75,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = result = isInCurrentFrame(p, n.sons[0]) else: discard -proc openArrayLoc(p: BProc, n: PNode): PRope = +proc openArrayLoc(p: BProc, n: PNode): Rope = var a: TLoc let q = skipConv(n) @@ -90,33 +98,34 @@ proc openArrayLoc(p: BProc, n: PNode): PRope = of tyOpenArray, tyVarargs, tyArray, tyArrayConstr: "($1)+($2), ($3)-($2)+1" of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar: + if skipTypes(n.typ, abstractInst).kind == tyVar and + not compileToCpp(p.module): "(*$1)->data+($2), ($3)-($2)+1" else: "$1->data+($2), ($3)-($2)+1" else: (internalError("openArrayLoc: " & typeToString(a.t)); "") - result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)]) + result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)] else: initLocExpr(p, n, a) case skipTypes(a.t, abstractVar).kind of tyOpenArray, tyVarargs: - result = ropef("$1, $1Len0", [rdLoc(a)]) + result = "$1, $1Len0" % [rdLoc(a)] of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar: - result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)]) + if skipTypes(n.typ, abstractInst).kind == tyVar and + not compileToCpp(p.module): + result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] else: - result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)]) + result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)] of tyArray, tyArrayConstr: - result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]) + result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] else: internalError("openArrayLoc: " & typeToString(a.t)) -proc genArgStringToCString(p: BProc, - n: PNode): PRope {.inline.} = +proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = var a: TLoc initLocExpr(p, n.sons[0], a) - result = ropef("$1->data", [a.rdLoc]) - -proc genArg(p: BProc, n: PNode, param: PSym): PRope = + result = "$1->data" % [a.rdLoc] + +proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = var a: TLoc if n.kind == nkStringToCString: result = genArgStringToCString(p, n) @@ -126,23 +135,35 @@ proc genArg(p: BProc, n: PNode, param: PSym): PRope = elif ccgIntroducedPtr(param): initLocExpr(p, n, a) result = addrLoc(a) + elif p.module.compileToCpp and param.typ.kind == tyVar and + n.kind == nkHiddenAddr: + initLocExprSingleUse(p, n.sons[0], a) + # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still + # means '*T'. See posix.nim for lots of examples that do that in the wild. + let callee = call.sons[0] + if callee.kind == nkSym and + {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and + {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: + result = addrLoc(a) + else: + result = rdLoc(a) else: - initLocExpr(p, n, a) + initLocExprSingleUse(p, n, a) result = rdLoc(a) -proc genArgNoParam(p: BProc, n: PNode): PRope = +proc genArgNoParam(p: BProc, n: PNode): Rope = var a: TLoc if n.kind == nkStringToCString: result = genArgStringToCString(p, n) else: - initLocExpr(p, n, a) + initLocExprSingleUse(p, n, a) result = rdLoc(a) proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op: TLoc # this is a hotspot in the compiler initLocExpr(p, ri.sons[0], op) - var params: PRope + var params: Rope # getUniqueType() is too expensive here: var typ = skipTypes(ri.sons[0].typ, abstractInst) assert(typ.kind == tyProc) @@ -150,48 +171,49 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = var length = sonsLen(ri) for i in countup(1, length - 1): if ri.sons[i].typ.isCompileTimeOnly: continue - if params != nil: app(params, ~", ") + if params != nil: add(params, ~", ") if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) - app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym)) + add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) else: - app(params, genArgNoParam(p, ri.sons[i])) + add(params, genArgNoParam(p, ri.sons[i])) fixupCall(p, le, ri, d, op.r, params) proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = - proc getRawProcType(p: BProc, t: PType): PRope = + proc getRawProcType(p: BProc, t: PType): Rope = result = getClosureType(p.module, t, clHalf) - proc addComma(r: PRope): PRope = - result = if r == nil: r else: con(r, ~", ") + proc addComma(r: Rope): Rope = + result = if r == nil: r else: r & ~", " const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)" const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists var op: TLoc initLocExpr(p, ri.sons[0], op) - var pl: PRope - + var pl: Rope + var typ = skipTypes(ri.sons[0].typ, abstractInst) assert(typ.kind == tyProc) var length = sonsLen(ri) for i in countup(1, length - 1): assert(sonsLen(typ) == sonsLen(typ.n)) + if ri.sons[i].typ.isCompileTimeOnly: continue if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) - app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym)) + add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) else: - app(pl, genArgNoParam(p, ri.sons[i])) - if i < length - 1: app(pl, ~", ") - + add(pl, genArgNoParam(p, ri.sons[i])) + if i < length - 1: add(pl, ~", ") + template genCallPattern {.dirty.} = - lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) + lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc]) let rawProc = getRawProcType(p, typ) let callPattern = if tfIterator in typ.flags: PatIter else: PatProc if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): - if sonsLen(ri) > 1: app(pl, ~", ") + if sonsLen(ri) > 1: add(pl, ~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): # Great, we can use 'd': @@ -200,12 +222,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: resetLoc(p, d) - app(pl, addrLoc(d)) + add(pl, addrLoc(d)) genCallPattern() else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - app(pl, addrLoc(tmp)) + add(pl, addrLoc(tmp)) genCallPattern() genAssignment(p, d, tmp, {}) # no need for deep copying else: @@ -213,37 +235,214 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, d.t, OnUnknown) - list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc) + list.r = callPattern % [op.r, pl, pl.addComma, rawProc] genAssignment(p, d, list, {}) # no need for deep copying else: genCallPattern() - + +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = + if ri.sons[i].typ.isCompileTimeOnly: + result = nil + elif i < sonsLen(typ): + # 'var T' is 'T&' in C++. This means we ignore the request of + # any nkHiddenAddr when it's a 'var T'. + assert(typ.n.sons[i].kind == nkSym) + if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr: + result = genArgNoParam(p, ri.sons[i][0]) + else: + result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) + else: + result = genArgNoParam(p, ri.sons[i]) + +discard """ +Dot call syntax in C++ +====================== + +so c2nim translates 'this' sometimes to 'T' and sometimes to 'var T' +both of which are wrong, but often more convenient to use. +For manual wrappers it can also be 'ptr T' + +Fortunately we know which parameter is the 'this' parameter and so can fix this +mess in the codegen. +now ... if the *argument* is a 'ptr' the codegen shall emit -> and otherwise . +but this only depends on the argument and not on how the 'this' was declared +however how the 'this' was declared affects whether we end up with +wrong 'addr' and '[]' ops... + +Since I'm tired I'll enumerate all the cases here: + +var + x: ptr T + y: T + +proc t(x: T) +x[].t() --> (*x).t() is correct. +y.t() --> y.t() is correct + +proc u(x: ptr T) +x.u() --> needs to become x->u() +(addr y).u() --> needs to become y.u() + +proc v(x: var T) +--> first skip the implicit 'nkAddr' node +x[].v() --> (*x).v() is correct, but might have been eliminated due + to the nkAddr node! So for this case we need to generate '->' +y.v() --> y.v() is correct + +""" + +proc skipAddrDeref(node: PNode): PNode = + var n = node + var isAddr = false + case n.kind + of nkAddr, nkHiddenAddr: + n = n.sons[0] + isAddr = true + of nkDerefExpr, nkHiddenDeref: + n = n.sons[0] + else: return n + if n.kind == nkObjDownConv: n = n.sons[0] + if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}: + result = n.sons[0] + elif n.kind in {nkAddr, nkHiddenAddr}: + result = n.sons[0] + else: + result = node + +proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = + # for better or worse c2nim translates the 'this' argument to a 'var T'. + # However manual wrappers may also use 'ptr T'. In any case we support both + # for convenience. + internalAssert i < sonsLen(typ) + assert(typ.n.sons[i].kind == nkSym) + # if the parameter is lying (tyVar) and thus we required an additional deref, + # skip the deref: + var ri = ri[i] + while ri.kind == nkObjDownConv: ri = ri[0] + let t = typ.sons[i].skipTypes({tyGenericInst}) + if t.kind == tyVar: + let x = if ri.kind == nkHiddenAddr: ri[0] else: ri + if x.typ.kind == tyPtr: + result = genArgNoParam(p, x) + result.add("->") + elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: + result = genArgNoParam(p, x[0]) + result.add("->") + else: + result = genArgNoParam(p, x) + result.add(".") + elif t.kind == tyPtr: + if ri.kind in {nkAddr, nkHiddenAddr}: + result = genArgNoParam(p, ri[0]) + result.add(".") + else: + result = genArgNoParam(p, ri) + result.add("->") + else: + ri = skipAddrDeref(ri) + if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0] + result = genArgNoParam(p, ri) #, typ.n.sons[i].sym) + result.add(".") + +proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope = + var i = 0 + var j = 1 + while i < pat.len: + case pat[i] + of '@': + if j < ri.len: + result.add genOtherArg(p, ri, j, typ) + for k in j+1 .. < ri.len: + result.add(~", ") + result.add genOtherArg(p, ri, k, typ) + inc i + of '#': + if pat[i+1] in {'+', '@'}: + let ri = ri[j] + if ri.kind in nkCallKinds: + let typ = skipTypes(ri.sons[0].typ, abstractInst) + if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0]) + result.add(~"(") + if 1 < ri.len: + result.add genOtherArg(p, ri, 1, typ) + for k in j+1 .. < ri.len: + result.add(~", ") + result.add genOtherArg(p, ri, k, typ) + result.add(~")") + else: + localError(ri.info, "call expression expected for C++ pattern") + inc i + elif pat[i+1] == '.': + result.add genThisArg(p, ri, j, typ) + inc i + elif pat[i+1] == '[': + var arg = ri.sons[j].skipAddrDeref + while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0] + result.add genArgNoParam(p, arg) + #result.add debugTree(arg, 0, 10) + else: + result.add genOtherArg(p, ri, j, typ) + inc j + inc i + of '\'': + var idx, stars: int + if scanCppGenericSlot(pat, i, idx, stars): + var t = resolveStarsInCppType(typ, idx, stars) + if t == nil: result.add(~"void") + else: result.add(getTypeDesc(p.module, t)) + else: + let start = i + while i < pat.len: + if pat[i] notin {'@', '#', '\''}: inc(i) + else: break + if i - 1 >= start: + add(result, substr(pat, start, i - 1)) + proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op, a: TLoc initLocExpr(p, ri.sons[0], op) - var pl: PRope = nil # getUniqueType() is too expensive here: var typ = skipTypes(ri.sons[0].typ, abstractInst) assert(typ.kind == tyProc) var length = sonsLen(ri) assert(sonsLen(typ) == sonsLen(typ.n)) - - var param = typ.n.sons[1].sym - app(pl, genArg(p, ri.sons[1], param)) - - if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, ~"->") - else: app(pl, ~".") - app(pl, op.r) - var params: PRope - for i in countup(2, length - 1): - if params != nil: params.app(~", ") - assert(sonsLen(typ) == sonsLen(typ.n)) - if i < sonsLen(typ): - assert(typ.n.sons[i].kind == nkSym) - app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym)) + # don't call '$' here for efficiency: + let pat = ri.sons[0].sym.loc.r.data + internalAssert pat != nil + if pat.contains({'#', '(', '@', '\''}): + var pl = genPatternCall(p, ri, pat, typ) + # simpler version of 'fixupCall' that works with the pl+params combination: + var typ = skipTypes(ri.sons[0].typ, abstractInst) + if typ.sons[0] != nil: + if p.module.compileToCpp and lfSingleUse in d.flags: + # do not generate spurious temporaries for C++! For C we're better off + # with them to prevent undefined behaviour and because the codegen + # is free to emit expressions multiple times! + d.k = locCall + d.r = pl + excl d.flags, lfSingleUse + else: + if d.k == locNone: getTemp(p, typ.sons[0], d) + assert(d.t != nil) # generate an assignment to d: + var list: TLoc + initLoc(list, locCall, d.t, OnUnknown) + list.r = pl + genAssignment(p, d, list, {}) # no need for deep copying else: - app(params, genArgNoParam(p, ri.sons[i])) - fixupCall(p, le, ri, d, pl, params) + add(pl, ~";$n") + line(p, cpsStmts, pl) + else: + var pl: Rope = nil + #var param = typ.n.sons[1].sym + if 1 < ri.len: + add(pl, genThisArg(p, ri, 1, typ)) + add(pl, op.r) + var params: Rope + for i in countup(2, length - 1): + if params != nil: params.add(~", ") + assert(sonsLen(typ) == sonsLen(typ.n)) + add(params, genOtherArg(p, ri, i, typ)) + fixupCall(p, le, ri, d, pl, params) proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = # generates a crappy ObjC call @@ -255,44 +454,56 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = assert(typ.kind == tyProc) var length = sonsLen(ri) assert(sonsLen(typ) == sonsLen(typ.n)) - - if length > 1: - app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym)) - app(pl, ~" ") - app(pl, op.r) - if length > 2: - app(pl, ~": ") - app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym)) - for i in countup(3, length-1): + + # don't call '$' here for efficiency: + let pat = ri.sons[0].sym.loc.r.data + internalAssert pat != nil + var start = 3 + if ' ' in pat: + start = 1 + add(pl, op.r) + if length > 1: + add(pl, ~": ") + add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) + start = 2 + else: + if length > 1: + add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) + add(pl, ~" ") + add(pl, op.r) + if length > 2: + add(pl, ~": ") + add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) + for i in countup(start, length-1): assert(sonsLen(typ) == sonsLen(typ.n)) if i >= sonsLen(typ): internalError(ri.info, "varargs for objective C method?") assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym - app(pl, ~" ") - app(pl, param.name.s) - app(pl, ~": ") - app(pl, genArg(p, ri.sons[i], param)) + add(pl, ~" ") + add(pl, param.name.s) + add(pl, ~": ") + add(pl, genArg(p, ri.sons[i], param, ri)) if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): - if sonsLen(ri) > 1: app(pl, ~" ") + if sonsLen(ri) > 1: add(pl, ~" ") # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: # We already got a temp. Great, special case it: if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true) - app(pl, ~"Result: ") - app(pl, addrLoc(d)) - app(pl, ~"];$n") + add(pl, ~"Result: ") + add(pl, addrLoc(d)) + add(pl, ~"];$n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - app(pl, addrLoc(tmp)) - app(pl, ~"];$n") + add(pl, addrLoc(tmp)) + add(pl, ~"];$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: - app(pl, ~"]") + add(pl, ~"]") if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc @@ -300,14 +511,13 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - app(pl, ~"];$n") + add(pl, ~"];$n") line(p, cpsStmts, pl) proc genCall(p: BProc, e: PNode, d: var TLoc) = if e.sons[0].typ.callConv == ccClosure: genClosureCall(p, nil, e, d) - elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and - e.len >= 2: + elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags: genInfixCall(p, nil, e, d) elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags: genNamedParamCall(p, e, d) @@ -320,8 +530,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) = proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = if ri.sons[0].typ.callConv == ccClosure: genClosureCall(p, le, ri, d) - elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and - ri.len >= 2: + elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags: genInfixCall(p, le, ri, d) elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags: genNamedParamCall(p, ri, d) |