mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 11:42:33 +00:00
* Fix typeSym.getImpl for ref types * Fix a codegen issue affecting the test suite of nim-beacon-chain * Fix tests/stdlib/tjsonmacro To understand the fix better it may help to take a look at the history of the replaced code. The nil check that is removed in this commit was introduced in another fix that failed to identify the root cause of the issue - namely that we allow an object type to exist for which no ast is present: https://github.com/nim-lang/Nim/pull/9601/files The original intention of the code is more obvious here: https://github.com/nim-lang/Nim/pull/9538/files
2998 lines
112 KiB
Nim
2998 lines
112 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2013 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# included from cgen.nim
|
|
|
|
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
|
|
result: var Rope; count: var int;
|
|
isConst: bool, info: TLineInfo)
|
|
|
|
# -------------------------- constant expressions ------------------------
|
|
|
|
proc int64Literal(i: BiggestInt): Rope =
|
|
if i > low(int64):
|
|
result = "IL64($1)" % [rope(i)]
|
|
else:
|
|
result = ~"(IL64(-9223372036854775807) - IL64(1))"
|
|
|
|
proc uint64Literal(i: uint64): Rope = rope($i & "ULL")
|
|
|
|
proc intLiteral(i: BiggestInt): Rope =
|
|
if i > low(int32) and i <= high(int32):
|
|
result = rope(i)
|
|
elif i == low(int32):
|
|
# Nim has the same bug for the same reasons :-)
|
|
result = ~"(-2147483647 -1)"
|
|
elif i > low(int64):
|
|
result = "IL64($1)" % [rope(i)]
|
|
else:
|
|
result = ~"(IL64(-9223372036854775807) - IL64(1))"
|
|
|
|
proc intLiteral(i: Int128): Rope =
|
|
intLiteral(toInt64(i))
|
|
|
|
proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
|
case n.kind
|
|
of nkCharLit..nkUInt64Lit:
|
|
var k: TTypeKind
|
|
if ty != nil:
|
|
k = skipTypes(ty, abstractVarRange).kind
|
|
else:
|
|
case n.kind
|
|
of nkCharLit: k = tyChar
|
|
of nkUInt64Lit: k = tyUInt64
|
|
of nkInt64Lit: k = tyInt64
|
|
else: k = tyNil # don't go into the case variant that uses 'ty'
|
|
case k
|
|
of tyChar, tyNil:
|
|
result = intLiteral(n.intVal)
|
|
of tyBool:
|
|
if n.intVal != 0: result = ~"NIM_TRUE"
|
|
else: result = ~"NIM_FALSE"
|
|
of tyInt64: result = int64Literal(n.intVal)
|
|
of tyUInt64: result = uint64Literal(uint64(n.intVal))
|
|
else:
|
|
result = "(($1) $2)" % [getTypeDesc(p.module,
|
|
ty), intLiteral(n.intVal)]
|
|
of nkNilLit:
|
|
let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind
|
|
if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure:
|
|
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
|
|
result = p.module.tmpBase & rope(id)
|
|
if id == p.module.labels:
|
|
# not found in cache:
|
|
inc(p.module.labels)
|
|
p.module.s[cfsData].addf(
|
|
"static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n",
|
|
[getTypeDesc(p.module, ty), result])
|
|
else:
|
|
result = rope("NIM_NIL")
|
|
of nkStrLit..nkTripleStrLit:
|
|
let k = if ty == nil: tyString
|
|
else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind
|
|
case k
|
|
of tyNil:
|
|
result = genNilStringLiteral(p.module, n.info)
|
|
of tyString:
|
|
# with the new semantics for 'nil' strings, we can map "" to nil and
|
|
# save tons of allocations:
|
|
if n.strVal.len == 0 and optNilSeqs notin p.options and
|
|
optSeqDestructors notin p.config.globalOptions:
|
|
result = genNilStringLiteral(p.module, n.info)
|
|
else:
|
|
result = genStringLiteral(p.module, n)
|
|
else:
|
|
result = makeCString(n.strVal)
|
|
of nkFloatLit, nkFloat64Lit:
|
|
result = rope(n.floatVal.toStrMaxPrecision)
|
|
of nkFloat32Lit:
|
|
result = rope(n.floatVal.toStrMaxPrecision("f"))
|
|
else:
|
|
internalError(p.config, n.info, "genLiteral(" & $n.kind & ')')
|
|
result = nil
|
|
|
|
proc genLiteral(p: BProc, n: PNode): Rope =
|
|
result = genLiteral(p, n, n.typ)
|
|
|
|
proc bitSetToWord(s: TBitSet, size: int): BiggestUInt =
|
|
result = 0
|
|
for j in 0..<size:
|
|
if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
|
|
|
|
proc genRawSetData(cs: TBitSet, size: int): Rope =
|
|
if size > 8:
|
|
var res = "{\n"
|
|
for i in 0..<size:
|
|
res.add "0x"
|
|
res.add "0123456789abcdef"[cs[i] div 16]
|
|
res.add "0123456789abcdef"[cs[i] mod 16]
|
|
if i < size - 1:
|
|
# not last iteration
|
|
if i mod 8 == 7:
|
|
res.add ",\n"
|
|
else:
|
|
res.add ", "
|
|
else:
|
|
res.add "}\n"
|
|
|
|
result = rope(res)
|
|
else:
|
|
result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size)))
|
|
|
|
proc genSetNode(p: BProc, n: PNode): Rope =
|
|
var cs: TBitSet
|
|
var size = int(getSize(p.config, n.typ))
|
|
toBitSet(p.config, n, cs)
|
|
if size > 8:
|
|
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
|
|
result = p.module.tmpBase & rope(id)
|
|
if id == p.module.labels:
|
|
# not found in cache:
|
|
inc(p.module.labels)
|
|
p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n",
|
|
[getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)])
|
|
else:
|
|
result = genRawSetData(cs, size)
|
|
|
|
proc getStorageLoc(n: PNode): TStorageLoc =
|
|
case n.kind
|
|
of nkSym:
|
|
case n.sym.kind
|
|
of skParam, skTemp:
|
|
result = OnStack
|
|
of skVar, skForVar, skResult, skLet:
|
|
if sfGlobal in n.sym.flags: result = OnHeap
|
|
else: result = OnStack
|
|
of skConst:
|
|
if sfGlobal in n.sym.flags: result = OnHeap
|
|
else: result = OnUnknown
|
|
else: result = OnUnknown
|
|
of nkDerefExpr, nkHiddenDeref:
|
|
case n[0].typ.kind
|
|
of tyVar, tyLent: result = OnUnknown
|
|
of tyPtr: result = OnStack
|
|
of tyRef: result = OnHeap
|
|
else: doAssert(false, "getStorageLoc")
|
|
of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
|
|
result = getStorageLoc(n[0])
|
|
else: result = OnUnknown
|
|
|
|
proc canMove(p: BProc, n: PNode; dest: TLoc): bool =
|
|
# for now we're conservative here:
|
|
if n.kind == nkBracket:
|
|
# This needs to be kept consistent with 'const' seq code
|
|
# generation!
|
|
if not isDeepConstExpr(n) or n.len == 0:
|
|
if skipTypes(n.typ, abstractVarRange).kind == tySequence:
|
|
return true
|
|
elif optNilSeqs notin p.options and
|
|
n.kind in nkStrKinds and n.strVal.len == 0:
|
|
# Empty strings are codegen'd as NIM_NIL so it's just a pointer copy
|
|
return true
|
|
result = n.kind in nkCallKinds
|
|
#if not result and dest.k == locTemp:
|
|
# return true
|
|
|
|
#if result:
|
|
# echo n.info, " optimized ", n
|
|
# result = false
|
|
|
|
proc genRefAssign(p: BProc, dest, src: TLoc) =
|
|
if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
elif dest.storage == OnHeap:
|
|
linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
|
|
[addrLoc(p.config, dest), rdLoc(src)])
|
|
else:
|
|
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
|
|
[addrLoc(p.config, dest), rdLoc(src)])
|
|
|
|
proc asgnComplexity(n: PNode): int =
|
|
if n != nil:
|
|
case n.kind
|
|
of nkSym: result = 1
|
|
of nkRecCase:
|
|
# 'case objects' are too difficult to inline their assignment operation:
|
|
result = 100
|
|
of nkRecList:
|
|
for t in items(n):
|
|
result += asgnComplexity(t)
|
|
else: discard
|
|
|
|
proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
|
|
assert field != nil
|
|
result.k = locField
|
|
result.storage = a.storage
|
|
result.lode = lodeTyp t
|
|
result.r = rdLoc(a) & "." & field
|
|
|
|
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
|
let newflags =
|
|
if src.storage == OnStatic:
|
|
flags + {needToCopy}
|
|
elif tfShallow in dest.t.flags:
|
|
flags - {needToCopy}
|
|
else:
|
|
flags
|
|
let t = skipTypes(dest.t, abstractInst).getUniqueType()
|
|
for i in 0..<t.len:
|
|
let t = t[i]
|
|
let field = "Field$1" % [i.rope]
|
|
genAssignment(p, optAsgnLoc(dest, t, field),
|
|
optAsgnLoc(src, t, field), newflags)
|
|
|
|
proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
|
|
t: PNode, typ: PType) =
|
|
if t == nil: return
|
|
let newflags =
|
|
if src.storage == OnStatic:
|
|
flags + {needToCopy}
|
|
elif tfShallow in dest.t.flags:
|
|
flags - {needToCopy}
|
|
else:
|
|
flags
|
|
case t.kind
|
|
of nkSym:
|
|
let field = t.sym
|
|
if field.loc.r == nil: fillObjectFields(p.module, typ)
|
|
genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
|
|
optAsgnLoc(src, field.typ, field.loc.r), newflags)
|
|
of nkRecList:
|
|
for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
|
|
else: discard
|
|
|
|
proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
|
# Consider:
|
|
# type TMyFastString {.shallow.} = string
|
|
# Due to the implementation of pragmas this would end up to set the
|
|
# tfShallow flag for the built-in string type too! So we check only
|
|
# here for this flag, where it is reasonably safe to do so
|
|
# (for objects, etc.):
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
linefmt(p, cpsStmts,
|
|
"$1 = $2;$n",
|
|
[rdLoc(dest), rdLoc(src)])
|
|
elif needToCopy notin flags or
|
|
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
|
|
if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
|
|
linefmt(p, cpsStmts,
|
|
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
|
[addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)])
|
|
else:
|
|
linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
|
|
[addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
else:
|
|
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
|
|
[addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
|
|
proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
|
# This function replaces all other methods for generating
|
|
# the assignment operation in C.
|
|
if src.t != nil and src.t.kind == tyPtr:
|
|
# little HACK to support the new 'var T' as return type:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
return
|
|
let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses + {tyStatic})
|
|
case ty.kind
|
|
of tyRef:
|
|
genRefAssign(p, dest, src)
|
|
of tySequence:
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
genGenericAsgn(p, dest, src, flags)
|
|
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
|
|
genRefAssign(p, dest, src)
|
|
else:
|
|
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
|
|
[addrLoc(p.config, dest), rdLoc(src),
|
|
genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
of tyString:
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
genGenericAsgn(p, dest, src, flags)
|
|
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
|
|
genRefAssign(p, dest, src)
|
|
else:
|
|
if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
|
|
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc])
|
|
elif dest.storage == OnHeap:
|
|
# we use a temporary to care for the dreaded self assignment:
|
|
var tmp: TLoc
|
|
getTemp(p, ty, tmp)
|
|
linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n",
|
|
[dest.rdLoc, src.rdLoc, tmp.rdLoc])
|
|
linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc])
|
|
else:
|
|
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
|
|
[addrLoc(p.config, dest), rdLoc(src)])
|
|
of tyProc:
|
|
if containsGarbageCollectedRef(dest.t):
|
|
# optimize closure assignment:
|
|
let a = optAsgnLoc(dest, dest.t, "ClE_0".rope)
|
|
let b = optAsgnLoc(src, dest.t, "ClE_0".rope)
|
|
genRefAssign(p, a, b)
|
|
linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", [rdLoc(dest), rdLoc(src)])
|
|
else:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
of tyTuple:
|
|
if containsGarbageCollectedRef(dest.t):
|
|
if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags)
|
|
else: genGenericAsgn(p, dest, src, flags)
|
|
else:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
of tyObject:
|
|
# XXX: check for subtyping?
|
|
if ty.isImportedCppType:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
elif not isObjLackingTypeField(ty):
|
|
genGenericAsgn(p, dest, src, flags)
|
|
elif containsGarbageCollectedRef(ty):
|
|
if ty[0].isNil and asgnComplexity(ty.n) <= 4:
|
|
discard getTypeDesc(p.module, ty)
|
|
internalAssert p.config, ty.n != nil
|
|
genOptAsgnObject(p, dest, src, flags, ty.n, ty)
|
|
else:
|
|
genGenericAsgn(p, dest, src, flags)
|
|
else:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
of tyArray:
|
|
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcOrc, gcHooks}:
|
|
genGenericAsgn(p, dest, src, flags)
|
|
else:
|
|
linefmt(p, cpsStmts,
|
|
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
|
[rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t)])
|
|
of tyOpenArray, tyVarargs:
|
|
# open arrays are always on the stack - really? What if a sequence is
|
|
# passed to an open array?
|
|
if containsGarbageCollectedRef(dest.t):
|
|
linefmt(p, cpsStmts, # XXX: is this correct for arrays?
|
|
"#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
|
|
[addrLoc(p.config, dest), addrLoc(p.config, src),
|
|
genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
else:
|
|
linefmt(p, cpsStmts,
|
|
# bug #4799, keep the nimCopyMem for a while
|
|
#"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
|
|
"$1 = $2;$n",
|
|
[rdLoc(dest), rdLoc(src)])
|
|
of tySet:
|
|
if mapType(p.config, ty) == ctArray:
|
|
linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
|
|
[rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
|
|
else:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
|
|
tyInt..tyUInt64, tyRange, tyVar, tyLent, tyNil:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
else: internalError(p.config, "genAssignment: " & $ty.kind)
|
|
|
|
if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}:
|
|
#writeStackTrace()
|
|
#echo p.currLineInfo, " requesting"
|
|
linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
|
|
[addrLoc(p.config, dest), getSize(p.config, dest.t),
|
|
makeCString(toFullPath(p.config, p.currLineInfo)),
|
|
p.currLineInfo.safeLineNm])
|
|
|
|
proc genDeepCopy(p: BProc; dest, src: TLoc) =
|
|
template addrLocOrTemp(a: TLoc): Rope =
|
|
if a.k == locExpr:
|
|
var tmp: TLoc
|
|
getTemp(p, a.t, tmp)
|
|
genAssignment(p, tmp, a, {})
|
|
addrLoc(p.config, tmp)
|
|
else:
|
|
addrLoc(p.config, a)
|
|
|
|
var ty = skipTypes(dest.t, abstractVarRange + {tyStatic})
|
|
case ty.kind
|
|
of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
|
|
# XXX optimize this
|
|
linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
|
|
[addrLoc(p.config, dest), addrLocOrTemp(src),
|
|
genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
of tySequence, tyString:
|
|
linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
|
|
[addrLoc(p.config, dest), rdLoc(src),
|
|
genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
of tyOpenArray, tyVarargs:
|
|
linefmt(p, cpsStmts,
|
|
"#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
|
|
[addrLoc(p.config, dest), addrLocOrTemp(src),
|
|
genTypeInfo(p.module, dest.t, dest.lode.info)])
|
|
of tySet:
|
|
if mapType(p.config, ty) == ctArray:
|
|
linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
|
|
[rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
|
|
else:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
of tyPointer, tyChar, tyBool, tyEnum, tyCString,
|
|
tyInt..tyUInt64, tyRange, tyVar, tyLent:
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
|
else: internalError(p.config, "genDeepCopy: " & $ty.kind)
|
|
|
|
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
|
|
if d.k != locNone:
|
|
if lfNoDeepCopy in d.flags: genAssignment(p, d, s, {})
|
|
else: genAssignment(p, d, s, {needToCopy})
|
|
else:
|
|
d = s # ``d`` is free, so fill it with ``s``
|
|
|
|
proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
|
|
var a: TLoc
|
|
if d.k != locNone:
|
|
# need to generate an assignment here
|
|
initLoc(a, locData, n, OnStatic)
|
|
a.r = r
|
|
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
|
|
else: genAssignment(p, d, a, {needToCopy})
|
|
else:
|
|
# we cannot call initLoc() here as that would overwrite
|
|
# the flags field!
|
|
d.k = locData
|
|
d.lode = n
|
|
d.r = r
|
|
|
|
proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
|
|
var a: TLoc
|
|
if d.k != locNone:
|
|
# need to generate an assignment here
|
|
initLoc(a, locExpr, n, s)
|
|
a.r = r
|
|
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
|
|
else: genAssignment(p, d, a, {needToCopy})
|
|
else:
|
|
# we cannot call initLoc() here as that would overwrite
|
|
# the flags field!
|
|
d.k = locExpr
|
|
d.lode = n
|
|
d.r = r
|
|
|
|
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) =
|
|
var a, b: TLoc
|
|
if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
lineCg(p, cpsStmts, "$1 $2 $3;$n", [rdLoc(a), op, rdLoc(b)])
|
|
|
|
proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) =
|
|
var a, b: TLoc
|
|
if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr")
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
lineCg(p, cpsStmts, "#$1($2, $3);$n", [cpname, byRefLoc(p, a), rdLoc(b)])
|
|
|
|
template unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
|
var a: TLoc
|
|
if d.k != locNone: internalError(p.config, e.info, "unaryStmt")
|
|
initLocExpr(p, e[1], a)
|
|
lineCg(p, cpsStmts, frmt, [rdLoc(a)])
|
|
|
|
template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
|
|
|
|
template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
|
|
|
|
template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)]))
|
|
|
|
template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)]))
|
|
|
|
template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
|
|
cpname: string): Rope =
|
|
var size = getSize(p.config, t)
|
|
let storage = if size < p.config.target.intSize: rope("NI")
|
|
else: getTypeDesc(p.module, t)
|
|
var result = getTempName(p.module)
|
|
linefmt(p, cpsLocals, "$1 $2;$n", [storage, result])
|
|
lineCg(p, cpsStmts, "if (#$2($3, $4, &$1)) { #raiseOverflow(); $5};$n",
|
|
[result, cpname, rdCharLoc(a), rdCharLoc(b), raiseInstr(p)])
|
|
if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}:
|
|
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); $4}$n",
|
|
[result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)),
|
|
raiseInstr(p)])
|
|
result
|
|
|
|
proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
|
const
|
|
prc: array[mAddI..mPred, string] = [
|
|
"nimAddInt", "nimSubInt",
|
|
"nimMulInt", "nimDivInt", "nimModInt",
|
|
"nimAddInt", "nimSubInt"
|
|
]
|
|
prc64: array[mAddI..mPred, string] = [
|
|
"nimAddInt64", "nimSubInt64",
|
|
"nimMulInt64", "nimDivInt64", "nimModInt64",
|
|
"nimAddInt64", "nimSubInt64"
|
|
]
|
|
opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
# skipping 'range' is correct here as we'll generate a proper range check
|
|
# later via 'chckRange'
|
|
let t = e.typ.skipTypes(abstractRange)
|
|
if optOverflowCheck notin p.options:
|
|
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
|
putIntoDest(p, d, e, res)
|
|
else:
|
|
# we handle div by zero here so that we know that the compilerproc's
|
|
# result is only for overflows.
|
|
if m in {mDivI, mModI}:
|
|
linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); $2}$n",
|
|
[rdLoc(b), raiseInstr(p)])
|
|
|
|
let res = binaryArithOverflowRaw(p, t, a, b,
|
|
if t.kind == tyInt64: prc64[m] else: prc[m])
|
|
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
|
|
|
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
|
var
|
|
a: TLoc
|
|
t: PType
|
|
assert(e[1].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
t = skipTypes(e.typ, abstractRange)
|
|
if optOverflowCheck in p.options:
|
|
linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); $3}$n",
|
|
[rdLoc(a), intLiteral(firstOrd(p.config, t)), raiseInstr(p)])
|
|
case m
|
|
of mUnaryMinusI:
|
|
putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
|
|
of mUnaryMinusI64:
|
|
putIntoDest(p, d, e, "-($1)" % [rdLoc(a)])
|
|
of mAbsI:
|
|
putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)])
|
|
else:
|
|
assert(false, $m)
|
|
|
|
proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|
var
|
|
a, b: TLoc
|
|
s, k: BiggestInt
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
# BUGFIX: cannot use result-type here, as it may be a boolean
|
|
s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
|
|
k = getSize(p.config, a.t) * 8
|
|
|
|
template applyFormat(frmt: untyped) =
|
|
putIntoDest(p, d, e, frmt % [
|
|
rdLoc(a), rdLoc(b), rope(s),
|
|
getSimpleTypeDesc(p.module, e.typ), rope(k)]
|
|
)
|
|
|
|
case op
|
|
of mAddF64: applyFormat("(($4)($1) + ($4)($2))")
|
|
of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
|
|
of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
|
|
of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
|
|
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
|
|
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
|
|
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
|
|
of mBitandI: applyFormat("($4)($1 & $2)")
|
|
of mBitorI: applyFormat("($4)($1 | $2)")
|
|
of mBitxorI: applyFormat("($4)($1 ^ $2)")
|
|
of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)")
|
|
of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)")
|
|
of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))")
|
|
of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))")
|
|
of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))")
|
|
of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))")
|
|
of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))")
|
|
of mEqI: applyFormat("($1 == $2)")
|
|
of mLeI: applyFormat("($1 <= $2)")
|
|
of mLtI: applyFormat("($1 < $2)")
|
|
of mEqF64: applyFormat("($1 == $2)")
|
|
of mLeF64: applyFormat("($1 <= $2)")
|
|
of mLtF64: applyFormat("($1 < $2)")
|
|
of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))")
|
|
of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))")
|
|
of mEqEnum: applyFormat("($1 == $2)")
|
|
of mLeEnum: applyFormat("($1 <= $2)")
|
|
of mLtEnum: applyFormat("($1 < $2)")
|
|
of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))")
|
|
of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))")
|
|
of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))")
|
|
of mEqB: applyFormat("($1 == $2)")
|
|
of mLeB: applyFormat("($1 <= $2)")
|
|
of mLtB: applyFormat("($1 < $2)")
|
|
of mEqRef: applyFormat("($1 == $2)")
|
|
of mLePtr: applyFormat("($1 <= $2)")
|
|
of mLtPtr: applyFormat("($1 < $2)")
|
|
of mXor: applyFormat("($1 != $2)")
|
|
else:
|
|
assert(false, $op)
|
|
|
|
proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
if a.t.skipTypes(abstractInstOwned).callConv == ccClosure:
|
|
putIntoDest(p, d, e,
|
|
"($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
|
|
else:
|
|
putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
|
|
|
|
proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
|
|
let t = skipTypes(e[1].typ, abstractRange)
|
|
if t.kind == tyProc and t.callConv == ccClosure:
|
|
unaryExpr(p, e, d, "($1.ClP_0 == 0)")
|
|
else:
|
|
unaryExpr(p, e, d, "($1 == 0)")
|
|
|
|
proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|
var
|
|
a: TLoc
|
|
t: PType
|
|
assert(e[1].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
t = skipTypes(e.typ, abstractRange)
|
|
|
|
template applyFormat(frmt: untyped) =
|
|
putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8),
|
|
getSimpleTypeDesc(p.module, e.typ)])
|
|
case op
|
|
of mNot:
|
|
applyFormat("!($1)")
|
|
of mUnaryPlusI:
|
|
applyFormat("$1")
|
|
of mBitnotI:
|
|
applyFormat("($3)((NU$2) ~($1))")
|
|
of mUnaryPlusF64:
|
|
applyFormat("$1")
|
|
of mUnaryMinusF64:
|
|
applyFormat("-($1)")
|
|
else:
|
|
assert false, $op
|
|
|
|
proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
|
|
result = p.module.compileToCpp and
|
|
skipTypes(typ, abstractInstOwned).kind == tyVar and
|
|
tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
|
|
|
|
proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
|
let mt = mapType(p.config, e[0].typ)
|
|
if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
|
|
# XXX the amount of hacks for C's arrays is incredible, maybe we should
|
|
# simply wrap them in a struct? --> Losing auto vectorization then?
|
|
expr(p, e[0], d)
|
|
if e[0].typ.skipTypes(abstractInstOwned).kind == tyRef:
|
|
d.storage = OnHeap
|
|
else:
|
|
var a: TLoc
|
|
var typ = e[0].typ
|
|
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
|
|
typ = typ.lastSon
|
|
typ = typ.skipTypes(abstractInstOwned)
|
|
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
|
|
initLocExprSingleUse(p, e[0][0], d)
|
|
return
|
|
else:
|
|
initLocExprSingleUse(p, e[0], a)
|
|
if d.k == locNone:
|
|
# dest = *a; <-- We do not know that 'dest' is on the heap!
|
|
# It is completely wrong to set 'd.storage' here, unless it's not yet
|
|
# been assigned to.
|
|
case typ.kind
|
|
of tyRef:
|
|
d.storage = OnHeap
|
|
of tyVar, tyLent:
|
|
d.storage = OnUnknown
|
|
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
|
|
e.kind == nkHiddenDeref:
|
|
putIntoDest(p, d, e, rdLoc(a), a.storage)
|
|
return
|
|
of tyPtr:
|
|
d.storage = OnUnknown # BUGFIX!
|
|
else:
|
|
internalError(p.config, e.info, "genDeref " & $typ.kind)
|
|
elif p.module.compileToCpp:
|
|
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
|
|
e.kind == nkHiddenDeref:
|
|
putIntoDest(p, d, e, rdLoc(a), a.storage)
|
|
return
|
|
if mt == ctPtrToArray and lfEnforceDeref in d.flags:
|
|
# we lie about the type for better C interop: 'ptr array[3,T]' is
|
|
# translated to 'ptr T', but for deref'ing this produces wrong code.
|
|
# See tmissingderef. So we get rid of the deref instead. The codegen
|
|
# ends up using 'memcpy' for the array assignment,
|
|
# so the '&' and '*' cancel out:
|
|
putIntoDest(p, d, lodeTyp(a.t[0]), rdLoc(a), a.storage)
|
|
else:
|
|
putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)
|
|
|
|
proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
|
# careful 'addr(myptrToArray)' needs to get the ampersand:
|
|
if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
|
|
var a: TLoc
|
|
initLocExpr(p, e[0], a)
|
|
putIntoDest(p, d, e, "&" & a.r, a.storage)
|
|
#Message(e.info, warnUser, "HERE NEW &")
|
|
elif mapType(p.config, e[0].typ) == ctArray or isCppRef(p, e.typ):
|
|
expr(p, e[0], d)
|
|
else:
|
|
var a: TLoc
|
|
initLocExpr(p, e[0], a)
|
|
putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
|
|
|
|
template inheritLocation(d: var TLoc, a: TLoc) =
|
|
if d.k == locNone: d.storage = a.storage
|
|
|
|
proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
|
|
initLocExpr(p, e[0], a)
|
|
if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux")
|
|
d.inheritLocation(a)
|
|
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
|
|
|
|
proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
|
|
var
|
|
a: TLoc
|
|
i: int
|
|
initLocExpr(p, e[0], a)
|
|
let tupType = a.t.skipTypes(abstractInst+{tyVar})
|
|
assert tupType.kind == tyTuple
|
|
d.inheritLocation(a)
|
|
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
|
|
var r = rdLoc(a)
|
|
case e[1].kind
|
|
of nkIntLit..nkUInt64Lit: i = int(e[1].intVal)
|
|
else: internalError(p.config, e.info, "genTupleElem")
|
|
r.addf(".Field$1", [rope(i)])
|
|
putIntoDest(p, d, e, r, a.storage)
|
|
|
|
proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
|
|
resTyp: ptr PType = nil): PSym =
|
|
var ty = ty
|
|
assert r != nil
|
|
while ty != nil:
|
|
ty = ty.skipTypes(skipPtrs)
|
|
assert(ty.kind in {tyTuple, tyObject})
|
|
result = lookupInRecord(ty.n, field.name)
|
|
if result != nil:
|
|
if resTyp != nil: resTyp[] = ty
|
|
break
|
|
if not p.module.compileToCpp: r.add(".Sup")
|
|
ty = ty[0]
|
|
if result == nil: internalError(p.config, field.info, "genCheckedRecordField")
|
|
|
|
proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
|
|
var a: TLoc
|
|
genRecordFieldAux(p, e, d, a)
|
|
var r = rdLoc(a)
|
|
var f = e[1].sym
|
|
let ty = skipTypes(a.t, abstractInstOwned + tyUserTypeClasses)
|
|
if ty.kind == tyTuple:
|
|
# we found a unique tuple type which lacks field information
|
|
# so we use Field$i
|
|
r.addf(".Field$1", [rope(f.position)])
|
|
putIntoDest(p, d, e, r, a.storage)
|
|
else:
|
|
var rtyp: PType
|
|
let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
|
|
if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp)
|
|
if field.loc.r == nil: internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
|
|
r.addf(".$1", [field.loc.r])
|
|
putIntoDest(p, d, e, r, a.storage)
|
|
|
|
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
|
|
|
|
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
|
|
var test, u, v: TLoc
|
|
for i in 1..<e.len:
|
|
var it = e[i]
|
|
assert(it.kind in nkCallKinds)
|
|
assert(it[0].kind == nkSym)
|
|
let op = it[0].sym
|
|
if op.magic == mNot: it = it[1]
|
|
let disc = it[2].skipConv
|
|
assert(disc.kind == nkSym)
|
|
initLoc(test, locNone, it, OnStack)
|
|
initLocExpr(p, it[1], u)
|
|
initLoc(v, locExpr, disc, OnUnknown)
|
|
v.r = obj
|
|
v.r.add(".")
|
|
v.r.add(disc.sym.loc.r)
|
|
genInExprAux(p, it, u, v, test)
|
|
let msg = genFieldError(field, disc.sym)
|
|
let strLit = genStringLiteral(p.module, newStrNode(nkStrLit, msg))
|
|
if op.magic == mNot:
|
|
linefmt(p, cpsStmts,
|
|
"if ($1){ #raiseFieldError($2); $3}$n",
|
|
[rdLoc(test), strLit, raiseInstr(p)])
|
|
else:
|
|
linefmt(p, cpsStmts,
|
|
"if (!($1)){ #raiseFieldError($2); $3}$n",
|
|
[rdLoc(test), strLit, raiseInstr(p)])
|
|
|
|
proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
|
if optFieldCheck in p.options:
|
|
var a: TLoc
|
|
genRecordFieldAux(p, e[0], d, a)
|
|
let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
|
|
var r = rdLoc(a)
|
|
let f = e[0][1].sym
|
|
let field = lookupFieldAgain(p, ty, f, r)
|
|
if field.loc.r == nil: fillObjectFields(p.module, ty)
|
|
if field.loc.r == nil:
|
|
internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
|
|
genFieldCheck(p, e, r, field)
|
|
r.add(ropecg(p.module, ".$1", [field.loc.r]))
|
|
putIntoDest(p, d, e[0], r, a.storage)
|
|
else:
|
|
genRecordField(p, e[0], d)
|
|
|
|
proc genUncheckedArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
|
var a, b: TLoc
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, y, b)
|
|
d.inheritLocation(a)
|
|
putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]),
|
|
a.storage)
|
|
|
|
proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
|
var a, b: TLoc
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, y, b)
|
|
var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
|
|
var first = intLiteral(firstOrd(p.config, ty))
|
|
# emit range check:
|
|
if optBoundsCheck in p.options and ty.kind != tyUncheckedArray:
|
|
if not isConstExpr(y):
|
|
# semantic pass has already checked for const index expressions
|
|
if firstOrd(p.config, ty) == 0:
|
|
if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
|
|
linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n",
|
|
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
|
else:
|
|
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); $4}$n",
|
|
[rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
|
else:
|
|
let idx = getOrdValue(y)
|
|
if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
|
|
localError(p.config, x.info, formatErrorIndexBound(idx, firstOrd(p.config, ty), lastOrd(p.config, ty)))
|
|
d.inheritLocation(a)
|
|
putIntoDest(p, d, n,
|
|
ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage)
|
|
|
|
proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
|
var a, b: TLoc
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, y, b)
|
|
inheritLocation(d, a)
|
|
putIntoDest(p, d, n,
|
|
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
|
|
|
|
proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
|
|
let ty = skipTypes(arr.t, abstractVarRange)
|
|
case ty.kind
|
|
of tyOpenArray, tyVarargs:
|
|
linefmt(p, cpsStmts,
|
|
"if ($2-$1 != -1 && " &
|
|
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n",
|
|
[rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)])
|
|
of tyArray:
|
|
let first = intLiteral(firstOrd(p.config, ty))
|
|
linefmt(p, cpsStmts,
|
|
"if ($2-$1 != -1 && " &
|
|
"($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); $5}$n",
|
|
[rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
|
of tySequence, tyString:
|
|
linefmt(p, cpsStmts,
|
|
"if ($2-$1 != -1 && " &
|
|
"((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)){ #raiseIndexError(); $4}$n",
|
|
[rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)])
|
|
else: discard
|
|
|
|
proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
|
var a, b: TLoc
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, y, b) # emit range check:
|
|
if optBoundsCheck in p.options:
|
|
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n",
|
|
[rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
|
|
inheritLocation(d, a)
|
|
putIntoDest(p, d, n,
|
|
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
|
|
|
|
proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
|
var a, b: TLoc
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, y, b)
|
|
var ty = skipTypes(a.t, abstractVarRange)
|
|
if ty.kind in {tyRef, tyPtr}:
|
|
ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
|
|
if optBoundsCheck in p.options:
|
|
if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
|
|
linefmt(p, cpsStmts,
|
|
"if ((NU)($1) > (NU)$2){ #raiseIndexError2($1,$2); $3}$n",
|
|
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
|
else:
|
|
linefmt(p, cpsStmts,
|
|
"if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n",
|
|
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
|
if d.k == locNone: d.storage = OnHeap
|
|
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
|
a.r = ropecg(p.module, "(*$1)", [a.r])
|
|
|
|
if lfPrepareForMutation in d.flags and ty.kind == tyString and
|
|
optSeqDestructors in p.config.globalOptions:
|
|
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
|
putIntoDest(p, d, n,
|
|
ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage)
|
|
|
|
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
|
|
var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
|
|
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
|
|
case ty.kind
|
|
of tyUncheckedArray: genUncheckedArrayElem(p, n, n[0], n[1], d)
|
|
of tyArray: genArrayElem(p, n, n[0], n[1], d)
|
|
of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n[0], n[1], d)
|
|
of tySequence, tyString: genSeqElem(p, n, n[0], n[1], d)
|
|
of tyCString: genCStringElem(p, n, n[0], n[1], d)
|
|
of tyTuple: genTupleElem(p, n, d)
|
|
else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
|
discard getTypeDesc(p.module, n.typ)
|
|
|
|
proc isSimpleExpr(n: PNode): bool =
|
|
# calls all the way down --> can stay expression based
|
|
case n.kind
|
|
of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr,
|
|
nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr,
|
|
nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr:
|
|
for c in n:
|
|
if not isSimpleExpr(c): return false
|
|
result = true
|
|
of nkStmtListExpr:
|
|
for i in 0..<n.len-1:
|
|
if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
|
|
result = isSimpleExpr(n.lastSon)
|
|
else:
|
|
if n.isAtom:
|
|
result = true
|
|
|
|
proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
|
# how to generate code?
|
|
# 'expr1 and expr2' becomes:
|
|
# result = expr1
|
|
# fjmp result, end
|
|
# result = expr2
|
|
# end:
|
|
# ... (result computed)
|
|
# BUGFIX:
|
|
# a = b or a
|
|
# used to generate:
|
|
# a = b
|
|
# if a: goto end
|
|
# a = a
|
|
# end:
|
|
# now it generates:
|
|
# tmp = b
|
|
# if tmp: goto end
|
|
# tmp = a
|
|
# end:
|
|
# a = tmp
|
|
when false:
|
|
#if isSimpleExpr(e) and p.module.compileToCpp:
|
|
var tmpA, tmpB: TLoc
|
|
#getTemp(p, e.typ, tmpA)
|
|
#getTemp(p, e.typ, tmpB)
|
|
initLocExprSingleUse(p, e[1], tmpA)
|
|
initLocExprSingleUse(p, e[2], tmpB)
|
|
tmpB.k = locExpr
|
|
if m == mOr:
|
|
tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
|
|
else:
|
|
tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
|
|
if d.k == locNone:
|
|
d = tmpB
|
|
else:
|
|
genAssignment(p, d, tmpB, {})
|
|
else:
|
|
var
|
|
L: TLabel
|
|
tmp: TLoc
|
|
getTemp(p, e.typ, tmp) # force it into a temp!
|
|
inc p.splitDecls
|
|
expr(p, e[1], tmp)
|
|
L = getLabel(p)
|
|
if m == mOr:
|
|
lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
|
|
else:
|
|
lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
|
|
expr(p, e[2], tmp)
|
|
fixLabel(p, L)
|
|
if d.k == locNone:
|
|
d = tmp
|
|
else:
|
|
genAssignment(p, d, tmp, {}) # no need for deep copying
|
|
dec p.splitDecls
|
|
|
|
proc genEcho(p: BProc, n: PNode) =
|
|
# this unusual way of implementing it ensures that e.g. ``echo("hallo", 45)``
|
|
# is threadsafe.
|
|
internalAssert p.config, n.kind == nkBracket
|
|
if p.config.target.targetOS == osGenode:
|
|
# echo directly to the Genode LOG session
|
|
var args: Rope = nil
|
|
var a: TLoc
|
|
for it in n.sons:
|
|
if it.skipConv.kind == nkNilLit:
|
|
args.add(", \"\"")
|
|
else:
|
|
initLocExpr(p, it, a)
|
|
args.add(ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)]))
|
|
p.module.includeHeader("<base/log.h>")
|
|
p.module.includeHeader("<util/string.h>")
|
|
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", [args])
|
|
else:
|
|
if n.len == 0:
|
|
linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len])
|
|
else:
|
|
var a: TLoc
|
|
initLocExpr(p, n, a)
|
|
linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len])
|
|
when false:
|
|
p.module.includeHeader("<stdio.h>")
|
|
linefmt(p, cpsStmts, "printf($1$2);$n",
|
|
makeCString(repeat("%s", n.len) & "\L"), [args])
|
|
linefmt(p, cpsStmts, "fflush(stdout);$n", [])
|
|
|
|
proc gcUsage(conf: ConfigRef; n: PNode) =
|
|
if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
|
|
|
|
proc strLoc(p: BProc; d: TLoc): Rope =
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
result = byRefLoc(p, d)
|
|
else:
|
|
result = rdLoc(d)
|
|
|
|
proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
|
|
# <Nim code>
|
|
# s = 'Hello ' & name & ', how do you feel?' & 'z'
|
|
#
|
|
# <generated C code>
|
|
# {
|
|
# string tmp0;
|
|
# ...
|
|
# tmp0 = rawNewString(6 + 17 + 1 + s2->len);
|
|
# // we cannot generate s = rawNewString(...) here, because
|
|
# // ``s`` may be used on the right side of the expression
|
|
# appendString(tmp0, strlit_1);
|
|
# appendString(tmp0, name);
|
|
# appendString(tmp0, strlit_2);
|
|
# appendChar(tmp0, 'z');
|
|
# asgn(s, tmp0);
|
|
# }
|
|
var a, tmp: TLoc
|
|
getTemp(p, e.typ, tmp)
|
|
var L = 0
|
|
var appends: Rope = nil
|
|
var lens: Rope = nil
|
|
for i in 0..<e.len - 1:
|
|
# compute the length expression:
|
|
initLocExpr(p, e[i + 1], a)
|
|
if skipTypes(e[i + 1].typ, abstractVarRange).kind == tyChar:
|
|
inc(L)
|
|
appends.add(ropecg(p.module, "#appendChar($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
|
|
else:
|
|
if e[i + 1].kind in {nkStrLit..nkTripleStrLit}:
|
|
inc(L, e[i + 1].strVal.len)
|
|
else:
|
|
lens.add(lenExpr(p, a))
|
|
lens.add(" + ")
|
|
appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
|
|
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L])
|
|
p.s(cpsStmts).add appends
|
|
if d.k == locNone:
|
|
d = tmp
|
|
else:
|
|
genAssignment(p, d, tmp, {}) # no need for deep copying
|
|
gcUsage(p.config, e)
|
|
|
|
proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
|
|
# <Nim code>
|
|
# s &= 'Hello ' & name & ', how do you feel?' & 'z'
|
|
# // BUG: what if s is on the left side too?
|
|
# <generated C code>
|
|
# {
|
|
# s = resizeString(s, 6 + 17 + 1 + name->len);
|
|
# appendString(s, strlit_1);
|
|
# appendString(s, name);
|
|
# appendString(s, strlit_2);
|
|
# appendChar(s, 'z');
|
|
# }
|
|
var
|
|
a, dest, call: TLoc
|
|
appends, lens: Rope
|
|
assert(d.k == locNone)
|
|
var L = 0
|
|
initLocExpr(p, e[1], dest)
|
|
for i in 0..<e.len - 2:
|
|
# compute the length expression:
|
|
initLocExpr(p, e[i + 2], a)
|
|
if skipTypes(e[i + 2].typ, abstractVarRange).kind == tyChar:
|
|
inc(L)
|
|
appends.add(ropecg(p.module, "#appendChar($1, $2);$n",
|
|
[strLoc(p, dest), rdLoc(a)]))
|
|
else:
|
|
if e[i + 2].kind in {nkStrLit..nkTripleStrLit}:
|
|
inc(L, e[i + 2].strVal.len)
|
|
else:
|
|
lens.add(lenExpr(p, a))
|
|
lens.add(" + ")
|
|
appends.add(ropecg(p.module, "#appendString($1, $2);$n",
|
|
[strLoc(p, dest), rdLoc(a)]))
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
|
|
[byRefLoc(p, dest), lens, L])
|
|
else:
|
|
initLoc(call, locCall, e, OnHeap)
|
|
call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
|
|
genAssignment(p, dest, call, {})
|
|
gcUsage(p.config, e)
|
|
p.s(cpsStmts).add appends
|
|
|
|
proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
|
|
# seq &= x -->
|
|
# seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
|
|
# seq->data[seq->len-1] = x;
|
|
var a, b, dest, tmpL, call: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
let seqType = skipTypes(e[1].typ, {tyVar})
|
|
initLoc(call, locCall, e, OnHeap)
|
|
if not p.module.compileToCpp:
|
|
const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
|
|
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
|
|
getTypeDesc(p.module, e[1].typ),
|
|
genTypeInfo(p.module, seqType, e.info)])
|
|
else:
|
|
const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
|
|
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
|
|
getTypeDesc(p.module, e[1].typ),
|
|
genTypeInfo(p.module, seqType, e.info)])
|
|
# emit the write barrier if required, but we can always move here, so
|
|
# use 'genRefAssign' for the seq.
|
|
genRefAssign(p, a, call)
|
|
#if bt != b.t:
|
|
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
|
|
initLoc(dest, locExpr, e[2], OnHeap)
|
|
getIntTemp(p, tmpL)
|
|
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)])
|
|
dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)])
|
|
genAssignment(p, dest, b, {needToCopy})
|
|
gcUsage(p.config, e)
|
|
|
|
proc genReset(p: BProc, n: PNode) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[1], a)
|
|
linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
|
|
[addrLoc(p.config, a),
|
|
genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)])
|
|
|
|
proc genDefault(p: BProc; n: PNode; d: var TLoc) =
|
|
if d.k == locNone: getTemp(p, n.typ, d, needsInit=true)
|
|
else: resetLoc(p, d)
|
|
|
|
proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
|
|
var sizeExpr = sizeExpr
|
|
let typ = a.t
|
|
var b: TLoc
|
|
initLoc(b, locExpr, a.lode, OnHeap)
|
|
let refType = typ.skipTypes(abstractInstOwned)
|
|
assert refType.kind == tyRef
|
|
let bt = refType.lastSon
|
|
if sizeExpr.isNil:
|
|
sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
|
|
|
|
if optTinyRtti in p.config.globalOptions:
|
|
if needsInit:
|
|
b.r = ropecg(p.module, "($1) #nimNewObj($2)",
|
|
[getTypeDesc(p.module, typ), sizeExpr])
|
|
else:
|
|
b.r = ropecg(p.module, "($1) #nimNewObjUninit($2)",
|
|
[getTypeDesc(p.module, typ), sizeExpr])
|
|
genAssignment(p, a, b, {})
|
|
else:
|
|
let ti = genTypeInfo(p.module, typ, a.lode.info)
|
|
if bt.destructor != nil and not isTrivialProc(bt.destructor):
|
|
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
|
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
|
# convention at least:
|
|
if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault:
|
|
localError(p.module.config, a.lode.info,
|
|
"the destructor that is turned into a finalizer needs " &
|
|
"to have the 'nimcall' calling convention")
|
|
var f: TLoc
|
|
initLocExpr(p, newSymNode(bt.destructor), f)
|
|
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
|
|
|
|
if a.storage == OnHeap and usesWriteBarrier(p.config):
|
|
if canFormAcycle(a.t):
|
|
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc])
|
|
else:
|
|
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc])
|
|
if p.config.selectedGC == gcGo:
|
|
# newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
|
|
# implement the write barrier
|
|
b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
|
|
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
|
|
[addrLoc(p.config, a), b.rdLoc])
|
|
else:
|
|
# use newObjRC1 as an optimization
|
|
b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
|
else:
|
|
b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
|
|
genAssignment(p, a, b, {})
|
|
# set the object type:
|
|
genObjectInit(p, cpsStmts, bt, a, constructRefObj)
|
|
|
|
proc genNew(p: BProc, e: PNode) =
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
# 'genNew' also handles 'unsafeNew':
|
|
if e.len == 3:
|
|
var se: TLoc
|
|
initLocExpr(p, e[2], se)
|
|
rawGenNew(p, a, se.rdLoc, needsInit = true)
|
|
else:
|
|
rawGenNew(p, a, nil, needsInit = true)
|
|
gcUsage(p.config, e)
|
|
|
|
proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
|
|
let seqtype = skipTypes(dest.t, abstractVarRange)
|
|
var call: TLoc
|
|
initLoc(call, locExpr, dest.lode, OnHeap)
|
|
if dest.storage == OnHeap and usesWriteBarrier(p.config):
|
|
if canFormAcycle(dest.t):
|
|
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
|
|
else:
|
|
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
|
|
if not lenIsZero:
|
|
if p.config.selectedGC == gcGo:
|
|
# we need the write barrier
|
|
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
|
|
genTypeInfo(p.module, seqtype, dest.lode.info), length])
|
|
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
|
|
else:
|
|
call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
|
|
genTypeInfo(p.module, seqtype, dest.lode.info), length])
|
|
linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
|
|
else:
|
|
if lenIsZero:
|
|
call.r = rope"NIM_NIL"
|
|
else:
|
|
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
|
|
genTypeInfo(p.module, seqtype, dest.lode.info), length])
|
|
genAssignment(p, dest, call, {})
|
|
|
|
proc genNewSeq(p: BProc, e: PNode) =
|
|
var a, b: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
let seqtype = skipTypes(e[1].typ, abstractVarRange)
|
|
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
|
[a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
|
getSeqPayloadType(p.module, seqtype)])
|
|
else:
|
|
let lenIsZero = optNilSeqs notin p.options and
|
|
e[2].kind == nkIntLit and e[2].intVal == 0
|
|
genNewSeqAux(p, a, b.rdLoc, lenIsZero)
|
|
gcUsage(p.config, e)
|
|
|
|
proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
|
|
let seqtype = skipTypes(e.typ, abstractVarRange)
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
|
|
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
|
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
|
getSeqPayloadType(p.module, seqtype)])
|
|
else:
|
|
putIntoDest(p, d, e, ropecg(p.module,
|
|
"($1)#nimNewSeqOfCap($2, $3)", [
|
|
getTypeDesc(p.module, seqtype),
|
|
genTypeInfo(p.module, seqtype, e.info), a.rdLoc]))
|
|
gcUsage(p.config, e)
|
|
|
|
proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
|
|
let t = n.typ
|
|
discard getTypeDesc(p.module, t) # so that any fields are initialized
|
|
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
|
|
fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic)
|
|
if id == p.module.labels:
|
|
# expression not found in the cache:
|
|
inc(p.module.labels)
|
|
p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n",
|
|
[getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true)])
|
|
|
|
proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
|
|
if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr:
|
|
rawConstExpr(p, n, d)
|
|
result = true
|
|
else:
|
|
result = false
|
|
|
|
proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
|
#echo rendertree e, " ", e.isDeepConstExpr
|
|
# inheritance in C++ does not allow struct initialization so
|
|
# we skip this step here:
|
|
if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions:
|
|
# disabled optimization: it is wrong for C++ and now also
|
|
# causes trouble for --gc:arc, see bug #13240
|
|
#[
|
|
var box: seq[Thing]
|
|
for i in 0..3:
|
|
box.add Thing(s1: "121") # pass by sink can mutate Thing.
|
|
]#
|
|
if handleConstExpr(p, e, d): return
|
|
var t = e.typ.skipTypes(abstractInstOwned)
|
|
let isRef = t.kind == tyRef
|
|
|
|
# check if we need to construct the object in a temporary
|
|
var useTemp =
|
|
isRef or
|
|
(d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
|
|
(isPartOf(d.lode, e) != arNo)
|
|
|
|
var tmp: TLoc
|
|
var r: Rope
|
|
if useTemp:
|
|
getTemp(p, t, tmp)
|
|
r = rdLoc(tmp)
|
|
if isRef:
|
|
rawGenNew(p, tmp, nil, needsInit = nfAllFieldsSet notin e.flags)
|
|
t = t.lastSon.skipTypes(abstractInstOwned)
|
|
r = "(*$1)" % [r]
|
|
gcUsage(p.config, e)
|
|
else:
|
|
constructLoc(p, tmp)
|
|
else:
|
|
resetLoc(p, d)
|
|
r = rdLoc(d)
|
|
discard getTypeDesc(p.module, t)
|
|
let ty = getUniqueType(t)
|
|
for i in 1..<e.len:
|
|
let it = e[i]
|
|
var tmp2: TLoc
|
|
tmp2.r = r
|
|
let field = lookupFieldAgain(p, ty, it[0].sym, tmp2.r)
|
|
if field.loc.r == nil: fillObjectFields(p.module, ty)
|
|
if field.loc.r == nil: internalError(p.config, e.info, "genObjConstr")
|
|
if it.len == 3 and optFieldCheck in p.options:
|
|
genFieldCheck(p, it[2], r, field)
|
|
tmp2.r.add(".")
|
|
tmp2.r.add(field.loc.r)
|
|
if useTemp:
|
|
tmp2.k = locTemp
|
|
tmp2.storage = if isRef: OnHeap else: OnStack
|
|
else:
|
|
tmp2.k = d.k
|
|
tmp2.storage = if isRef: OnHeap else: d.storage
|
|
tmp2.lode = it[1]
|
|
expr(p, it[1], tmp2)
|
|
if useTemp:
|
|
if d.k == locNone:
|
|
d = tmp
|
|
else:
|
|
genAssignment(p, d, tmp, {})
|
|
|
|
proc lhsDoesAlias(a, b: PNode): bool =
|
|
for y in b:
|
|
if isPartOf(a, y) != arNo: return true
|
|
|
|
proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
|
|
var arr, tmp: TLoc
|
|
# bug #668
|
|
let doesAlias = lhsDoesAlias(d.lode, n)
|
|
let dest = if doesAlias: addr(tmp) else: addr(d)
|
|
if doesAlias:
|
|
getTemp(p, n.typ, tmp)
|
|
elif d.k == locNone:
|
|
getTemp(p, n.typ, d)
|
|
|
|
let l = intLiteral(n.len)
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
let seqtype = n.typ
|
|
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
|
[rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon),
|
|
getSeqPayloadType(p.module, seqtype)])
|
|
else:
|
|
# generate call to newSeq before adding the elements per hand:
|
|
genNewSeqAux(p, dest[], l,
|
|
optNilSeqs notin p.options and n.len == 0)
|
|
for i in 0..<n.len:
|
|
initLoc(arr, locExpr, n[i], OnHeap)
|
|
arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), intLiteral(i), dataField(p)])
|
|
arr.storage = OnHeap # we know that sequences are on the heap
|
|
expr(p, n[i], arr)
|
|
gcUsage(p.config, n)
|
|
if doesAlias:
|
|
if d.k == locNone:
|
|
d = tmp
|
|
else:
|
|
genAssignment(p, d, tmp, {})
|
|
|
|
proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
|
|
var elem, a, arr: TLoc
|
|
if n[1].kind == nkBracket:
|
|
n[1].typ = n.typ
|
|
genSeqConstr(p, n[1], d)
|
|
return
|
|
if d.k == locNone:
|
|
getTemp(p, n.typ, d)
|
|
# generate call to newSeq before adding the elements per hand:
|
|
let L = toInt(lengthOrd(p.config, n[1].typ))
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
let seqtype = n.typ
|
|
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
|
[rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon),
|
|
getSeqPayloadType(p.module, seqtype)])
|
|
else:
|
|
genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0)
|
|
initLocExpr(p, n[1], a)
|
|
# bug #5007; do not produce excessive C source code:
|
|
if L < 10:
|
|
for i in 0..<L:
|
|
initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
|
|
elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), intLiteral(i), dataField(p)])
|
|
elem.storage = OnHeap # we know that sequences are on the heap
|
|
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
|
|
arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), intLiteral(i)])
|
|
genAssignment(p, elem, arr, {needToCopy})
|
|
else:
|
|
var i: TLoc
|
|
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i)
|
|
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L])
|
|
initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
|
|
elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
|
|
elem.storage = OnHeap # we know that sequences are on the heap
|
|
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
|
|
arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
|
|
genAssignment(p, elem, arr, {needToCopy})
|
|
lineF(p, cpsStmts, "}$n", [])
|
|
|
|
|
|
proc genNewFinalize(p: BProc, e: PNode) =
|
|
var
|
|
a, b, f: TLoc
|
|
refType, bt: PType
|
|
ti: Rope
|
|
refType = skipTypes(e[1].typ, abstractVarRange)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], f)
|
|
initLoc(b, locExpr, a.lode, OnHeap)
|
|
ti = genTypeInfo(p.module, refType, e.info)
|
|
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
|
|
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
|
|
getTypeDesc(p.module, refType),
|
|
ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))])
|
|
genAssignment(p, a, b, {}) # set the object type:
|
|
bt = skipTypes(refType.lastSon, abstractRange)
|
|
genObjectInit(p, cpsStmts, bt, a, constructRefObj)
|
|
gcUsage(p.config, e)
|
|
|
|
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
|
|
if optTinyRtti in p.config.globalOptions:
|
|
result = ropecg(p.module, "#isObj($1.m_type, $2)",
|
|
[a, genTypeInfo2Name(p.module, dest)])
|
|
else:
|
|
# unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
|
|
# have to call it here first:
|
|
let ti = genTypeInfo(p.module, dest, info)
|
|
if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
|
|
tfObjHasKids notin dest.flags):
|
|
result = "$1.m_type == $2" % [a, ti]
|
|
else:
|
|
discard cgsym(p.module, "TNimType")
|
|
inc p.module.labels
|
|
let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
|
|
p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache])
|
|
result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache])
|
|
when false:
|
|
# former version:
|
|
result = ropecg(p.module, "#isObj($1.m_type, $2)",
|
|
[a, genTypeInfo(p.module, dest, info)])
|
|
|
|
proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
|
|
var a: TLoc
|
|
initLocExpr(p, x, a)
|
|
var dest = skipTypes(typ, typedescPtrs)
|
|
var r = rdLoc(a)
|
|
var nilCheck: Rope = nil
|
|
var t = skipTypes(a.t, abstractInstOwned)
|
|
while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
|
|
if t.kind notin {tyVar, tyLent}: nilCheck = r
|
|
if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
|
|
r = ropecg(p.module, "(*$1)", [r])
|
|
t = skipTypes(t.lastSon, typedescInst+{tyOwned})
|
|
discard getTypeDesc(p.module, t)
|
|
if not p.module.compileToCpp:
|
|
while t.kind == tyObject and t[0] != nil:
|
|
r.add(~".Sup")
|
|
t = skipTypes(t[0], skipPtrs)
|
|
if isObjLackingTypeField(t):
|
|
globalError(p.config, x.info,
|
|
"no 'of' operator available for pure objects")
|
|
if nilCheck != nil:
|
|
r = ropecg(p.module, "(($1) && ($2))", [nilCheck, genOfHelper(p, dest, r, x.info)])
|
|
else:
|
|
r = ropecg(p.module, "($1)", [genOfHelper(p, dest, r, x.info)])
|
|
putIntoDest(p, d, x, r, a.storage)
|
|
|
|
proc genOf(p: BProc, n: PNode, d: var TLoc) =
|
|
genOf(p, n[1], n[2].typ, d)
|
|
|
|
proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
|
if optTinyRtti in p.config.globalOptions:
|
|
localError(p.config, e.info, "'repr' is not available for --newruntime")
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
var t = skipTypes(e[1].typ, abstractVarRange)
|
|
case t.kind
|
|
of tyInt..tyInt64, tyUInt..tyUInt64:
|
|
putIntoDest(p, d, e,
|
|
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage)
|
|
of tyFloat..tyFloat128:
|
|
putIntoDest(p, d, e, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage)
|
|
of tyBool:
|
|
putIntoDest(p, d, e, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage)
|
|
of tyChar:
|
|
putIntoDest(p, d, e, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage)
|
|
of tyEnum, tyOrdinal:
|
|
putIntoDest(p, d, e,
|
|
ropecg(p.module, "#reprEnum((NI)$1, $2)", [
|
|
rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
|
|
of tyString:
|
|
putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
|
|
of tySet:
|
|
putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
|
|
addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
|
|
of tyOpenArray, tyVarargs:
|
|
var b: TLoc
|
|
case skipTypes(a.t, abstractVarRange).kind
|
|
of tyOpenArray, tyVarargs:
|
|
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
|
|
of tyString, tySequence:
|
|
putIntoDest(p, b, e,
|
|
"$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage)
|
|
of tyArray:
|
|
putIntoDest(p, b, e,
|
|
"$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
|
|
else: internalError(p.config, e[0].info, "genRepr()")
|
|
putIntoDest(p, d, e,
|
|
ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
|
|
genTypeInfo(p.module, elemType(t), e.info)]), a.storage)
|
|
of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
|
|
putIntoDest(p, d, e,
|
|
ropecg(p.module, "#reprAny($1, $2)", [
|
|
rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
|
|
of tyEmpty, tyVoid:
|
|
localError(p.config, e.info, "'repr' doesn't support 'void' type")
|
|
else:
|
|
putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
|
|
[addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]),
|
|
a.storage)
|
|
gcUsage(p.config, e)
|
|
|
|
proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope): Rope =
|
|
result = rdLoc(a)
|
|
var t = skipTypes(a.t, abstractInst)
|
|
while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
|
|
if t.kind notin {tyVar, tyLent}: nilCheck = result
|
|
if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
|
|
result = "(*$1)" % [result]
|
|
t = skipTypes(t.lastSon, abstractInst)
|
|
discard getTypeDesc(p.module, t)
|
|
if not p.module.compileToCpp:
|
|
while t.kind == tyObject and t[0] != nil:
|
|
result.add(".Sup")
|
|
t = skipTypes(t[0], skipPtrs)
|
|
result.add ".m_type"
|
|
|
|
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
|
|
discard cgsym(p.module, "TNimType")
|
|
let t = e[1].typ
|
|
if isFinal(t) or e[0].sym.name.s != "getDynamicTypeInfo":
|
|
# ordinary static type information
|
|
putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info))
|
|
else:
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
var nilCheck = Rope(nil)
|
|
# use the dynamic type stored at offset 0:
|
|
putIntoDest(p, d, e, rdMType(p, a, nilCheck))
|
|
|
|
template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[1], a)
|
|
a.r = ropecg(p.module, frmt, [rdLoc(a)])
|
|
a.flags = a.flags - {lfIndirect} # this flag should not be propagated here (not just for HCR)
|
|
if d.k == locNone: getTemp(p, n.typ, d)
|
|
genAssignment(p, d, a, {})
|
|
gcUsage(p.config, n)
|
|
|
|
proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|
var a = e[1]
|
|
if a.kind == nkHiddenAddr: a = a[0]
|
|
var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
|
|
case typ.kind
|
|
of tyOpenArray, tyVarargs:
|
|
# Bug #9279, len(toOpenArray()) has to work:
|
|
if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice:
|
|
# magic: pass slice to openArray:
|
|
var b, c: TLoc
|
|
initLocExpr(p, a[2], b)
|
|
initLocExpr(p, a[3], c)
|
|
if op == mHigh:
|
|
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)]))
|
|
else:
|
|
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
|
|
else:
|
|
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
|
|
else: unaryExpr(p, e, d, "$1Len_0")
|
|
of tyCString:
|
|
if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
|
|
else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
|
|
of tyString:
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
var x = lenExpr(p, a)
|
|
if op == mHigh: x = "($1-1)" % [x]
|
|
putIntoDest(p, d, e, x)
|
|
of tySequence:
|
|
# we go through a temporary here because people write bullshit code.
|
|
var a, tmp: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
getIntTemp(p, tmp)
|
|
var x = lenExpr(p, a)
|
|
if op == mHigh: x = "($1-1)" % [x]
|
|
lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x])
|
|
putIntoDest(p, d, e, tmp.r)
|
|
of tyArray:
|
|
# YYY: length(sideeffect) is optimized away incorrectly?
|
|
if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
|
|
else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
|
|
else: internalError(p.config, e.info, "genArrayLen()")
|
|
|
|
proc makePtrType(baseType: PType): PType =
|
|
result = newType(tyPtr, baseType.owner)
|
|
addSonSkipIntLit(result, baseType)
|
|
|
|
proc makeAddr(n: PNode): PNode =
|
|
if n.kind == nkHiddenAddr:
|
|
result = n
|
|
else:
|
|
result = newTree(nkHiddenAddr, n)
|
|
result.typ = makePtrType(n.typ)
|
|
|
|
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
e[1] = makeAddr(e[1])
|
|
genCall(p, e, d)
|
|
return
|
|
var a, b, call: TLoc
|
|
assert(d.k == locNone)
|
|
var x = e[1]
|
|
if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, e[2], b)
|
|
let t = skipTypes(e[1].typ, {tyVar})
|
|
|
|
initLoc(call, locCall, e, OnHeap)
|
|
if not p.module.compileToCpp:
|
|
const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
|
|
call.r = ropecg(p.module, setLenPattern, [
|
|
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
|
genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
|
|
|
|
else:
|
|
const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
|
|
call.r = ropecg(p.module, setLenPattern, [
|
|
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
|
genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
|
|
|
|
genAssignment(p, a, call, {})
|
|
gcUsage(p.config, e)
|
|
|
|
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
binaryStmtAddr(p, e, d, "setLengthStrV2")
|
|
else:
|
|
var a, b, call: TLoc
|
|
if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
|
|
initLoc(call, locCall, e, OnHeap)
|
|
call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
|
|
rdLoc(a), rdLoc(b)])
|
|
genAssignment(p, a, call, {})
|
|
gcUsage(p.config, e)
|
|
|
|
proc genSwap(p: BProc, e: PNode, d: var TLoc) =
|
|
# swap(a, b) -->
|
|
# temp = a
|
|
# a = b
|
|
# b = temp
|
|
var a, b, tmp: TLoc
|
|
getTemp(p, skipTypes(e[1].typ, abstractVar), tmp)
|
|
initLocExpr(p, e[1], a) # eval a
|
|
initLocExpr(p, e[2], b) # eval b
|
|
genAssignment(p, tmp, a, {})
|
|
genAssignment(p, a, b, {})
|
|
genAssignment(p, b, tmp, {})
|
|
|
|
proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType): Rope =
|
|
# read a location of an set element; it may need a subtraction operation
|
|
# before the set operation
|
|
result = rdCharLoc(a)
|
|
let setType = typ.skipTypes(abstractPtrs)
|
|
assert(setType.kind == tySet)
|
|
if firstOrd(conf, setType) != 0:
|
|
result = "($1- $2)" % [result, rope(firstOrd(conf, setType))]
|
|
|
|
proc fewCmps(conf: ConfigRef; s: PNode): bool =
|
|
# this function estimates whether it is better to emit code
|
|
# for constructing the set or generating a bunch of comparisons directly
|
|
if s.kind != nkCurly: return false
|
|
if (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
|
|
result = false # it is better to emit the set generation code
|
|
elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
|
|
result = true # better not emit the set if int is basetype!
|
|
else:
|
|
result = s.len <= 8 # 8 seems to be a good value
|
|
|
|
template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
|
|
putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
|
|
|
|
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
|
case int(getSize(p.config, skipTypes(e[1].typ, abstractVar)))
|
|
of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
|
|
of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
|
|
of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
|
|
of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
|
|
else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
|
|
|
|
template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
|
var a, b: TLoc
|
|
assert(d.k == locNone)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
|
|
|
|
proc genInOp(p: BProc, e: PNode, d: var TLoc) =
|
|
var a, b, x, y: TLoc
|
|
if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]):
|
|
# a set constructor but not a constant set:
|
|
# do not emit the set, but generate a bunch of comparisons; and if we do
|
|
# so, we skip the unnecessary range check: This is a semantical extension
|
|
# that code now relies on. :-/ XXX
|
|
let ea = if e[2].kind in {nkChckRange, nkChckRange64}:
|
|
e[2][0]
|
|
else:
|
|
e[2]
|
|
initLocExpr(p, ea, a)
|
|
initLoc(b, locExpr, e, OnUnknown)
|
|
if e[1].len > 0:
|
|
b.r = rope("(")
|
|
for i in 0..<e[1].len:
|
|
let it = e[1][i]
|
|
if it.kind == nkRange:
|
|
initLocExpr(p, it[0], x)
|
|
initLocExpr(p, it[1], y)
|
|
b.r.addf("$1 >= $2 && $1 <= $3",
|
|
[rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
|
|
else:
|
|
initLocExpr(p, it, x)
|
|
b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
|
|
if i < e[1].len - 1: b.r.add(" || ")
|
|
b.r.add(")")
|
|
else:
|
|
# handle the case of an empty set
|
|
b.r = rope("0")
|
|
putIntoDest(p, d, e, b.r)
|
|
else:
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
genInExprAux(p, e, a, b, d)
|
|
|
|
proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|
const
|
|
lookupOpr: array[mLeSet..mMinusSet, string] = [
|
|
"for ($1 = 0; $1 < $2; $1++) { $n" &
|
|
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
|
|
" if (!$3) break;}$n",
|
|
"for ($1 = 0; $1 < $2; $1++) { $n" &
|
|
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
|
|
" if (!$3) break;}$n" &
|
|
"if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
|
|
"&",
|
|
"|",
|
|
"& ~"]
|
|
var a, b, i: TLoc
|
|
var setType = skipTypes(e[1].typ, abstractVar)
|
|
var size = int(getSize(p.config, setType))
|
|
case size
|
|
of 1, 2, 4, 8:
|
|
case op
|
|
of mIncl:
|
|
case size
|
|
of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n")
|
|
of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n")
|
|
of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n")
|
|
of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n")
|
|
else: assert(false, $size)
|
|
of mExcl:
|
|
case size
|
|
of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n")
|
|
of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n")
|
|
of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n")
|
|
of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n")
|
|
else: assert(false, $size)
|
|
of mCard:
|
|
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
|
|
else: unaryExprChar(p, e, d, "#countBits64($1)")
|
|
of mLtSet: binaryExprChar(p, e, d, "((($1 & ~ $2)==0)&&($1 != $2))")
|
|
of mLeSet: binaryExprChar(p, e, d, "(($1 & ~ $2)==0)")
|
|
of mEqSet: binaryExpr(p, e, d, "($1 == $2)")
|
|
of mMulSet: binaryExpr(p, e, d, "($1 & $2)")
|
|
of mPlusSet: binaryExpr(p, e, d, "($1 | $2)")
|
|
of mMinusSet: binaryExpr(p, e, d, "($1 & ~ $2)")
|
|
of mInSet:
|
|
genInOp(p, e, d)
|
|
else: internalError(p.config, e.info, "genSetOp()")
|
|
else:
|
|
case op
|
|
of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
|
|
of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n")
|
|
of mCard:
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size]))
|
|
of mLtSet, mLeSet:
|
|
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d)
|
|
if op == mLtSet:
|
|
linefmt(p, cpsStmts, lookupOpr[mLtSet],
|
|
[rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
|
|
else:
|
|
linefmt(p, cpsStmts, lookupOpr[mLeSet],
|
|
[rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
|
|
of mEqSet:
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
|
|
of mMulSet, mPlusSet, mMinusSet:
|
|
# we inline the simple for loop for better code generation:
|
|
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
if d.k == locNone: getTemp(p, setType, d)
|
|
lineF(p, cpsStmts,
|
|
"for ($1 = 0; $1 < $2; $1++) $n" &
|
|
" $3[$1] = $4[$1] $6 $5[$1];$n", [
|
|
rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b),
|
|
rope(lookupOpr[op])])
|
|
of mInSet: genInOp(p, e, d)
|
|
else: internalError(p.config, e.info, "genSetOp")
|
|
|
|
proc genOrd(p: BProc, e: PNode, d: var TLoc) =
|
|
unaryExprChar(p, e, d, "$1")
|
|
|
|
proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
|
const
|
|
ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray}
|
|
# we use whatever C gives us. Except if we have a value-type, we need to go
|
|
# through its address:
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
|
|
let srcTyp = skipTypes(e[1].typ, abstractRange)
|
|
if etyp.kind in ValueTypes and lfIndirect notin a.flags:
|
|
putIntoDest(p, d, e, "(*($1*) ($2))" %
|
|
[getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
|
|
elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
|
|
putIntoDest(p, d, e, "(($1) ($2))" %
|
|
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
|
else:
|
|
# C++ does not like direct casts from pointer to shorter integral types
|
|
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
|
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
|
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
|
elif optSeqDestructors in p.config.globalOptions and etyp.kind in {tySequence, tyString}:
|
|
putIntoDest(p, d, e, "(*($1*) (&$2))" %
|
|
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
|
elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes:
|
|
putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage)
|
|
else:
|
|
putIntoDest(p, d, e, "(($1) ($2))" %
|
|
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
|
|
|
proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
|
const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
|
|
let
|
|
destt = skipTypes(e.typ, abstractRange)
|
|
srct = skipTypes(e[1].typ, abstractRange)
|
|
if destt.kind in ValueTypes or srct.kind in ValueTypes:
|
|
# 'cast' and some float type involved? --> use a union.
|
|
inc(p.labels)
|
|
var lbl = p.labels.rope
|
|
var tmp: TLoc
|
|
tmp.r = "LOC$1.source" % [lbl]
|
|
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
|
|
[getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
|
|
tmp.k = locExpr
|
|
tmp.lode = lodeTyp srct
|
|
tmp.storage = OnStack
|
|
tmp.flags = {}
|
|
expr(p, e[1], tmp)
|
|
putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage)
|
|
else:
|
|
# I prefer the shorter cast version for pointer types -> generate less
|
|
# C code; plus it's the right thing to do for closures:
|
|
genSomeCast(p, e, d)
|
|
|
|
proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
|
|
var a: TLoc
|
|
var dest = skipTypes(n.typ, abstractVar)
|
|
initLocExpr(p, n[0], a)
|
|
if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
|
|
checkUnsignedConversions notin p.config.legacyFeatures):
|
|
discard "no need to generate a check because it was disabled"
|
|
else:
|
|
let raiser =
|
|
case skipTypes(n.typ, abstractVarRange).kind
|
|
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
|
|
of tyFloat..tyFloat128: "raiseRangeErrorF"
|
|
else: "raiseRangeErrorI"
|
|
discard cgsym(p.module, raiser)
|
|
# This seems to be bug-compatible with Nim version 1 but what we
|
|
# should really do here is to check if uint64Value < high(int)
|
|
let n0t = n[0].typ
|
|
let boundaryCast =
|
|
if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64} or
|
|
(n0t.sym != nil and sfSystemModule in n0t.sym.owner.flags and n0t.sym.name.s == "csize"):
|
|
"(NI64)"
|
|
else:
|
|
""
|
|
# emit range check:
|
|
linefmt(p, cpsStmts, "if ($6($1) < $2 || $6($1) > $3){ $4($1, $2, $3); $5}$n",
|
|
[rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest),
|
|
raiser, raiseInstr(p), boundaryCast])
|
|
putIntoDest(p, d, n, "(($1) ($2))" %
|
|
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
|
|
|
proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
|
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
|
|
if sameBackendType(destType, e[1].typ):
|
|
expr(p, e[1], d)
|
|
else:
|
|
genSomeCast(p, e, d)
|
|
|
|
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[0], a)
|
|
putIntoDest(p, d, n,
|
|
ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]),
|
|
# "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
|
|
a.storage)
|
|
|
|
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[0], a)
|
|
putIntoDest(p, d, n,
|
|
ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
|
|
a.storage)
|
|
gcUsage(p.config, n)
|
|
|
|
proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
|
|
var x: TLoc
|
|
var a = e[1]
|
|
var b = e[2]
|
|
if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
|
|
initLocExpr(p, e[2], x)
|
|
putIntoDest(p, d, e,
|
|
ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
|
|
elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
|
|
initLocExpr(p, e[1], x)
|
|
putIntoDest(p, d, e,
|
|
ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
|
|
else:
|
|
binaryExpr(p, e, d, "#eqStrings($1, $2)")
|
|
|
|
proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
|
if {optNaNCheck, optInfCheck} * p.options != {}:
|
|
const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"]
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))",
|
|
[opr[m], rdLoc(a), rdLoc(b),
|
|
getSimpleTypeDesc(p.module, e[1].typ)]))
|
|
if optNaNCheck in p.options:
|
|
linefmt(p, cpsStmts, "if ($1 != $1){ #raiseFloatInvalidOp(); $2}$n", [rdLoc(d), raiseInstr(p)])
|
|
if optInfCheck in p.options:
|
|
linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); $2}$n", [rdLoc(d), raiseInstr(p)])
|
|
else:
|
|
binaryArith(p, e, d, m)
|
|
|
|
proc skipAddr(n: PNode): PNode =
|
|
result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
|
|
|
|
proc genWasMoved(p: BProc; n: PNode) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[1].skipAddr, a)
|
|
resetLoc(p, a)
|
|
#linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
|
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
|
|
|
|
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[1].skipAddr, a)
|
|
if n.len == 4:
|
|
# generated by liftdestructors:
|
|
var src: TLoc
|
|
initLocExpr(p, n[2], src)
|
|
linefmt(p, cpsStmts, "if ($1.len && $1.p != $2.p) {", [rdLoc(a), rdLoc(src)])
|
|
genStmts(p, n[3])
|
|
linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
|
|
else:
|
|
if d.k == locNone: getTemp(p, n.typ, d)
|
|
genAssignment(p, d, a, {})
|
|
resetLoc(p, a)
|
|
|
|
proc genDestroy(p: BProc; n: PNode) =
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
let arg = n[1].skipAddr
|
|
let t = arg.typ.skipTypes(abstractInst)
|
|
case t.kind
|
|
of tyString:
|
|
var a: TLoc
|
|
initLocExpr(p, arg, a)
|
|
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
|
|
" #deallocShared($1.p);$n" &
|
|
" $1.p = NIM_NIL; $1.len = 0; }$n",
|
|
[rdLoc(a)])
|
|
of tySequence:
|
|
var a: TLoc
|
|
initLocExpr(p, arg, a)
|
|
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
|
|
" #deallocShared($1.p);$n" &
|
|
" $1.p = NIM_NIL; $1.len = 0; }$n",
|
|
[rdLoc(a), getTypeDesc(p.module, t.lastSon)])
|
|
else: discard "nothing to do"
|
|
else:
|
|
let t = n[1].typ.skipTypes(abstractVar)
|
|
if t.destructor != nil and t.destructor.ast[bodyPos].len != 0:
|
|
internalError(p.config, n.info, "destructor turned out to be not trivial")
|
|
discard "ignore calls to the default destructor"
|
|
|
|
proc genDispose(p: BProc; n: PNode) =
|
|
when false:
|
|
let elemType = n[1].typ.skipTypes(abstractVar).lastSon
|
|
|
|
var a: TLoc
|
|
initLocExpr(p, n[1].skipAddr, a)
|
|
|
|
if isFinal(elemType):
|
|
if elemType.destructor != nil:
|
|
var destroyCall = newNodeI(nkCall, n.info)
|
|
genStmts(p, destroyCall)
|
|
lineCg(p, cpsStmts, ["#nimRawDispose($#)", rdLoc(a)])
|
|
else:
|
|
# ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the
|
|
# destructor, but it uses the runtime type. Afterwards the memory is freed:
|
|
lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)])
|
|
|
|
proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
|
|
const ToStringProcSlot = -4
|
|
let t = e[1].typ.skipTypes(abstractInst+{tyRange})
|
|
var toStrProc: PSym = nil
|
|
for idx, p in items(t.methods):
|
|
if idx == ToStringProcSlot:
|
|
toStrProc = p
|
|
break
|
|
if toStrProc == nil:
|
|
toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph)
|
|
t.methods.add((ToStringProcSlot, toStrProc))
|
|
var n = copyTree(e)
|
|
n[0] = newSymNode(toStrProc)
|
|
expr(p, n, d)
|
|
|
|
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|
case op
|
|
of mOr, mAnd: genAndOr(p, e, d, op)
|
|
of mNot..mUnaryMinusF64: unaryArith(p, e, d, op)
|
|
of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
|
|
of mAddF64..mDivF64: binaryFloatArith(p, e, d, op)
|
|
of mShrI..mXor: binaryArith(p, e, d, op)
|
|
of mEqProc: genEqProc(p, e, d)
|
|
of mAddI..mPred: binaryArithOverflow(p, e, d, op)
|
|
of mRepr: genRepr(p, e, d)
|
|
of mGetTypeInfo: genGetTypeInfo(p, e, d)
|
|
of mSwap: genSwap(p, e, d)
|
|
of mInc, mDec:
|
|
const opr: array[mInc..mDec, string] = ["+=", "-="]
|
|
const fun64: array[mInc..mDec, string] = ["nimAddInt64", "nimSubInt64"]
|
|
const fun: array[mInc..mDec, string] = ["nimAddInt","nimSubInt"]
|
|
let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange})
|
|
if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
|
|
binaryStmt(p, e, d, opr[op])
|
|
else:
|
|
var a, b: TLoc
|
|
assert(e[1].typ != nil)
|
|
assert(e[2].typ != nil)
|
|
initLocExpr(p, e[1], a)
|
|
initLocExpr(p, e[2], b)
|
|
|
|
let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent})
|
|
let res = binaryArithOverflowRaw(p, ranged, a, b,
|
|
if underlying.kind == tyInt64: fun64[op] else: fun[op])
|
|
|
|
putIntoDest(p, a, e[1], "($#)($#)" % [
|
|
getTypeDesc(p.module, ranged), res])
|
|
|
|
of mConStrStr: genStrConcat(p, e, d)
|
|
of mAppendStrCh:
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
binaryStmtAddr(p, e, d, "nimAddCharV1")
|
|
else:
|
|
var dest, b, call: TLoc
|
|
initLoc(call, locCall, e, OnHeap)
|
|
initLocExpr(p, e[1], dest)
|
|
initLocExpr(p, e[2], b)
|
|
call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
|
|
genAssignment(p, dest, call, {})
|
|
of mAppendStrStr: genStrAppend(p, e, d)
|
|
of mAppendSeqElem:
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
e[1] = makeAddr(e[1])
|
|
genCall(p, e, d)
|
|
else:
|
|
genSeqElemAppend(p, e, d)
|
|
of mEqStr: genStrEquals(p, e, d)
|
|
of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)")
|
|
of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)")
|
|
of mIsNil: genIsNil(p, e, d)
|
|
of mIntToStr: genDollar(p, e, d, "#nimIntToStr($1)")
|
|
of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)")
|
|
of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)")
|
|
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
|
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
|
|
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
|
of mStrToStr, mUnown: expr(p, e[1], d)
|
|
of mEnumToStr:
|
|
if optTinyRtti in p.config.globalOptions:
|
|
genEnumToStr(p, e, d)
|
|
else:
|
|
genRepr(p, e, d)
|
|
of mOf: genOf(p, e, d)
|
|
of mNew: genNew(p, e)
|
|
of mNewFinalize:
|
|
if optTinyRtti in p.config.globalOptions:
|
|
var a: TLoc
|
|
initLocExpr(p, e[1], a)
|
|
rawGenNew(p, a, nil, needsInit = true)
|
|
gcUsage(p.config, e)
|
|
else:
|
|
genNewFinalize(p, e)
|
|
of mNewSeq: genNewSeq(p, e)
|
|
of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
|
|
of mSizeOf:
|
|
let t = e[1].typ.skipTypes({tyTypeDesc})
|
|
putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
|
|
of mAlignOf:
|
|
let t = e[1].typ.skipTypes({tyTypeDesc})
|
|
if not p.module.compileToCpp:
|
|
p.module.includeHeader("<stdalign.h>")
|
|
putIntoDest(p, d, e, "((NI)alignof($1))" % [getTypeDesc(p.module, t)])
|
|
of mOffsetOf:
|
|
var dotExpr: PNode
|
|
block findDotExpr:
|
|
if e[1].kind == nkDotExpr:
|
|
dotExpr = e[1]
|
|
elif e[1].kind == nkCheckedFieldExpr:
|
|
dotExpr = e[1][0]
|
|
else:
|
|
internalError(p.config, e.info, "unknown ast")
|
|
let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
|
|
let tname = getTypeDesc(p.module, t)
|
|
let member =
|
|
if t.kind == tyTuple:
|
|
"Field" & rope(dotExpr[1].sym.position)
|
|
else: dotExpr[1].sym.loc.r
|
|
putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
|
|
of mChr: genSomeCast(p, e, d)
|
|
of mOrd: genOrd(p, e, d)
|
|
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
|
|
genArrayLen(p, e, d, op)
|
|
of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
|
|
of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
|
|
of mSetLengthStr: genSetLengthStr(p, e, d)
|
|
of mSetLengthSeq: genSetLengthSeq(p, e, d)
|
|
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
|
|
mInSet:
|
|
genSetOp(p, e, d, op)
|
|
of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
|
|
var opr = e[0].sym
|
|
# Why would anyone want to set nodecl to one of these hardcoded magics?
|
|
# - not sure, and it wouldn't work if the symbol behind the magic isn't
|
|
# somehow forward-declared from some other usage, but it is *possible*
|
|
if lfNoDecl notin opr.loc.flags:
|
|
let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r)
|
|
# HACK:
|
|
# Explicitly add this proc as declared here so the cgsym call doesn't
|
|
# add a forward declaration - without this we could end up with the same
|
|
# 2 forward declarations. That happens because the magic symbol and the original
|
|
# one that shall be used have different ids (even though a call to one is
|
|
# actually a call to the other) so checking into m.declaredProtos with the 2 different ids doesn't work.
|
|
# Why would 2 identical forward declarations be a problem?
|
|
# - in the case of hot code-reloading we generate function pointers instead
|
|
# of forward declarations and in C++ it is an error to redefine a global
|
|
let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id)
|
|
# Make the function behind the magic get actually generated - this will
|
|
# not lead to a forward declaration! The genCall will lead to one.
|
|
discard cgsym(p.module, $opr.loc.r)
|
|
# make sure we have pointer-initialising code for hot code reloading
|
|
if not wasDeclared and p.hcrOn:
|
|
p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
|
|
[mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)])
|
|
genCall(p, e, d)
|
|
of mDefault: genDefault(p, e, d)
|
|
of mReset: genReset(p, e)
|
|
of mEcho: genEcho(p, e[1].skipConv)
|
|
of mArrToSeq: genArrToSeq(p, e, d)
|
|
of mNLen..mNError, mSlurp..mQuoteAst:
|
|
localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s))
|
|
of mSpawn:
|
|
when defined(leanCompiler):
|
|
quit "compiler built without support for the 'spawn' statement"
|
|
else:
|
|
let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil)
|
|
expr(p, n, d)
|
|
of mParallel:
|
|
when defined(leanCompiler):
|
|
quit "compiler built without support for the 'parallel' statement"
|
|
else:
|
|
let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e)
|
|
expr(p, n, d)
|
|
of mDeepCopy:
|
|
var a, b: TLoc
|
|
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
|
|
initLocExpr(p, x, a)
|
|
initLocExpr(p, e[2], b)
|
|
genDeepCopy(p, a, b)
|
|
of mDotDot, mEqCString: genCall(p, e, d)
|
|
of mWasMoved: genWasMoved(p, e)
|
|
of mMove: genMove(p, e, d)
|
|
of mDestroy: genDestroy(p, e)
|
|
of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0")
|
|
of mSlice:
|
|
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
|
|
"'toOpenArray' is only valid within a call expression")
|
|
else:
|
|
when defined(debugMagics):
|
|
echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
|
|
internalError(p.config, e.info, "genMagicExpr: " & $op)
|
|
|
|
proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
|
# example: { a..b, c, d, e, f..g }
|
|
# we have to emit an expression of the form:
|
|
# nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
|
|
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
|
|
var
|
|
a, b, idx: TLoc
|
|
if nfAllConst in e.flags:
|
|
putIntoDest(p, d, e, genSetNode(p, e))
|
|
else:
|
|
if d.k == locNone: getTemp(p, e.typ, d)
|
|
if getSize(p.config, e.typ) > 8:
|
|
# big set:
|
|
linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
|
[rdLoc(d), getTypeDesc(p.module, e.typ)])
|
|
for it in e.sons:
|
|
if it.kind == nkRange:
|
|
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter
|
|
initLocExpr(p, it[0], a)
|
|
initLocExpr(p, it[1], b)
|
|
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
|
|
"$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
|
|
rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)])
|
|
else:
|
|
initLocExpr(p, it, a)
|
|
lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
|
|
[rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
|
|
else:
|
|
# small set
|
|
var ts = "NU" & $(getSize(p.config, e.typ) * 8)
|
|
lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
|
|
for it in e.sons:
|
|
if it.kind == nkRange:
|
|
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter
|
|
initLocExpr(p, it[0], a)
|
|
initLocExpr(p, it[1], b)
|
|
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
|
|
"$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [
|
|
rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
|
|
rdSetElemLoc(p.config, b, e.typ), rope(ts)])
|
|
else:
|
|
initLocExpr(p, it, a)
|
|
lineF(p, cpsStmts,
|
|
"$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n",
|
|
[rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)])
|
|
|
|
proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
|
var rec: TLoc
|
|
if not handleConstExpr(p, n, d):
|
|
let t = n.typ
|
|
discard getTypeDesc(p.module, t) # so that any fields are initialized
|
|
if d.k == locNone: getTemp(p, t, d)
|
|
for i in 0..<n.len:
|
|
var it = n[i]
|
|
if it.kind == nkExprColonExpr: it = it[1]
|
|
initLoc(rec, locExpr, it, d.storage)
|
|
rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
|
|
rec.flags.incl(lfEnforceDeref)
|
|
expr(p, it, rec)
|
|
|
|
proc isConstClosure(n: PNode): bool {.inline.} =
|
|
result = n[0].kind == nkSym and isRoutine(n[0].sym) and
|
|
n[1].kind == nkNilLit
|
|
|
|
proc genClosure(p: BProc, n: PNode, d: var TLoc) =
|
|
assert n.kind in {nkPar, nkTupleConstr, nkClosure}
|
|
|
|
if isConstClosure(n):
|
|
inc(p.module.labels)
|
|
var tmp = "CNSTCLOSURE" & rope(p.module.labels)
|
|
p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n",
|
|
[getTypeDesc(p.module, n.typ), tmp, genBracedInit(p, n, isConst = true)])
|
|
putIntoDest(p, d, n, tmp, OnStatic)
|
|
else:
|
|
var tmp, a, b: TLoc
|
|
initLocExpr(p, n[0], a)
|
|
initLocExpr(p, n[1], b)
|
|
if n[0].skipConv.kind == nkClosure:
|
|
internalError(p.config, n.info, "closure to closure created")
|
|
# tasyncawait.nim breaks with this optimization:
|
|
when false:
|
|
if d.k != locNone:
|
|
linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n",
|
|
[d.rdLoc, a.rdLoc, b.rdLoc])
|
|
else:
|
|
getTemp(p, n.typ, tmp)
|
|
linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n",
|
|
[tmp.rdLoc, a.rdLoc, b.rdLoc])
|
|
putLocIntoDest(p, d, tmp)
|
|
|
|
proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
|
|
var arr: TLoc
|
|
if not handleConstExpr(p, n, d):
|
|
if d.k == locNone: getTemp(p, n.typ, d)
|
|
for i in 0..<n.len:
|
|
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
|
|
arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)]
|
|
expr(p, n[i], arr)
|
|
|
|
proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
|
|
requestConstImpl(p, sym)
|
|
assert((sym.loc.r != nil) and (sym.loc.t != nil))
|
|
putLocIntoDest(p, d, sym.loc)
|
|
|
|
template genStmtListExprImpl(exprOrStmt) {.dirty.} =
|
|
#let hasNimFrame = magicsys.getCompilerProc("nimFrame") != nil
|
|
let hasNimFrame = p.prc != nil and
|
|
sfSystemModule notin p.module.module.flags and
|
|
optStackTrace in p.prc.options
|
|
var frameName: Rope = nil
|
|
for i in 0..<n.len - 1:
|
|
let it = n[i]
|
|
if it.kind == nkComesFrom:
|
|
if hasNimFrame and frameName == nil:
|
|
inc p.labels
|
|
frameName = "FR" & rope(p.labels) & "_"
|
|
let theMacro = it[0].sym
|
|
add p.s(cpsStmts), initFrameNoDebug(p, frameName,
|
|
makeCString theMacro.name.s,
|
|
quotedFilename(p.config, theMacro.info), it.info.line.int)
|
|
else:
|
|
genStmts(p, it)
|
|
if n.len > 0: exprOrStmt
|
|
if frameName != nil:
|
|
p.s(cpsStmts).add deinitFrameNoDebug(p, frameName)
|
|
|
|
proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) =
|
|
genStmtListExprImpl:
|
|
expr(p, n[^1], d)
|
|
|
|
proc genStmtList(p: BProc, n: PNode) =
|
|
genStmtListExprImpl:
|
|
genStmts(p, n[^1])
|
|
|
|
proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
|
var a: TLoc
|
|
initLocExpr(p, n[0], a)
|
|
let dest = skipTypes(n.typ, abstractPtrs)
|
|
if optObjCheck in p.options and not isObjLackingTypeField(dest):
|
|
var nilCheck = Rope(nil)
|
|
let r = rdMType(p, a, nilCheck)
|
|
let checkFor = if optTinyRtti in p.config.globalOptions:
|
|
genTypeInfo2Name(p.module, dest)
|
|
else:
|
|
genTypeInfo(p.module, dest, n.info)
|
|
if nilCheck != nil:
|
|
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); $4}$n",
|
|
[nilCheck, r, checkFor, raiseInstr(p)])
|
|
else:
|
|
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); $3}$n",
|
|
[r, checkFor, raiseInstr(p)])
|
|
if n[0].typ.kind != tyObject:
|
|
putIntoDest(p, d, n,
|
|
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
|
else:
|
|
putIntoDest(p, d, n, "(*($1*) ($2))" %
|
|
[getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
|
|
|
|
proc downConv(p: BProc, n: PNode, d: var TLoc) =
|
|
if p.module.compileToCpp:
|
|
discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs))
|
|
expr(p, n[0], d) # downcast does C++ for us
|
|
else:
|
|
var dest = skipTypes(n.typ, abstractPtrs)
|
|
|
|
var arg = n[0]
|
|
while arg.kind == nkObjDownConv: arg = arg[0]
|
|
|
|
var src = skipTypes(arg.typ, abstractPtrs)
|
|
discard getTypeDesc(p.module, src)
|
|
var a: TLoc
|
|
initLocExpr(p, arg, a)
|
|
var r = rdLoc(a)
|
|
let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}
|
|
if isRef:
|
|
r.add("->Sup")
|
|
else:
|
|
r.add(".Sup")
|
|
for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup")
|
|
if isRef:
|
|
# it can happen that we end up generating '&&x->Sup' here, so we pack
|
|
# the '&x->Sup' into a temporary and then those address is taken
|
|
# (see bug #837). However sometimes using a temporary is not correct:
|
|
# init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
|
|
# this by ensuring the destination is also a pointer:
|
|
if d.k == locNone and skipTypes(n.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}:
|
|
getTemp(p, n.typ, d)
|
|
linefmt(p, cpsStmts, "$1 = &$2;$n", [rdLoc(d), r])
|
|
else:
|
|
r = "&" & r
|
|
putIntoDest(p, d, n, r, a.storage)
|
|
else:
|
|
putIntoDest(p, d, n, r, a.storage)
|
|
|
|
proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
|
|
let t = n.typ
|
|
discard getTypeDesc(p.module, t) # so that any fields are initialized
|
|
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
|
|
let tmp = p.module.tmpBase & rope(id)
|
|
|
|
if id == p.module.labels:
|
|
# expression not found in the cache:
|
|
inc(p.module.labels)
|
|
p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n",
|
|
[getTypeDesc(p.module, t), tmp, genBracedInit(p, n, isConst = true)])
|
|
|
|
if d.k == locNone:
|
|
fillLoc(d, locData, n, tmp, OnStatic)
|
|
else:
|
|
putDataIntoDest(p, d, n, tmp)
|
|
# This fixes bug #4551, but we really need better dataflow
|
|
# analysis to make this 100% safe.
|
|
if t.kind notin {tySequence, tyString}:
|
|
d.storage = OnStatic
|
|
|
|
proc expr(p: BProc, n: PNode, d: var TLoc) =
|
|
p.currLineInfo = n.info
|
|
|
|
case n.kind
|
|
of nkSym:
|
|
var sym = n.sym
|
|
case sym.kind
|
|
of skMethod:
|
|
if {sfDispatcher, sfForward} * sym.flags != {}:
|
|
# we cannot produce code for the dispatcher yet:
|
|
fillProcLoc(p.module, n)
|
|
genProcPrototype(p.module, sym)
|
|
else:
|
|
genProc(p.module, sym)
|
|
putLocIntoDest(p, d, sym.loc)
|
|
of skProc, skConverter, skIterator, skFunc:
|
|
#if sym.kind == skIterator:
|
|
# echo renderTree(sym.getBody, {renderIds})
|
|
if sfCompileTime in sym.flags:
|
|
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
|
|
sym.name.s)
|
|
genProc(p.module, sym)
|
|
if sym.loc.r == nil or sym.loc.lode == nil:
|
|
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
|
|
putLocIntoDest(p, d, sym.loc)
|
|
of skConst:
|
|
if isSimpleConst(sym.typ):
|
|
putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
|
|
else:
|
|
genComplexConst(p, sym, d)
|
|
of skEnumField:
|
|
# we never reach this case - as of the time of this comment,
|
|
# skEnumField is folded to an int in semfold.nim, but this code
|
|
# remains for robustness
|
|
putIntoDest(p, d, n, rope(sym.position))
|
|
of skVar, skForVar, skResult, skLet:
|
|
if {sfGlobal, sfThread} * sym.flags != {}:
|
|
genVarPrototype(p.module, n)
|
|
if sfCompileTime in sym.flags:
|
|
genSingleVar(p, sym, n, astdef(sym))
|
|
|
|
if sym.loc.r == nil or sym.loc.t == nil:
|
|
#echo "FAILED FOR PRCO ", p.prc.name.s
|
|
#echo renderTree(p.prc.ast, {renderIds})
|
|
internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
|
|
if sfThread in sym.flags:
|
|
accessThreadLocalVar(p, sym)
|
|
if emulatedThreadVars(p.config):
|
|
putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
|
|
else:
|
|
putLocIntoDest(p, d, sym.loc)
|
|
else:
|
|
putLocIntoDest(p, d, sym.loc)
|
|
of skTemp:
|
|
if sym.loc.r == nil:
|
|
# we now support undeclared 'skTemp' variables for easier
|
|
# transformations in other parts of the compiler:
|
|
assignLocalVar(p, n)
|
|
if sym.loc.r == nil or sym.loc.t == nil:
|
|
#echo "FAILED FOR PRCO ", p.prc.name.s
|
|
#echo renderTree(p.prc.ast, {renderIds})
|
|
internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
|
|
putLocIntoDest(p, d, sym.loc)
|
|
of skParam:
|
|
if sym.loc.r == nil or sym.loc.t == nil:
|
|
# echo "FAILED FOR PRCO ", p.prc.name.s
|
|
# debug p.prc.typ.n
|
|
# echo renderTree(p.prc.ast, {renderIds})
|
|
internalError(p.config, n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
|
|
putLocIntoDest(p, d, sym.loc)
|
|
else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol")
|
|
of nkNilLit:
|
|
if not isEmptyType(n.typ):
|
|
putIntoDest(p, d, n, genLiteral(p, n))
|
|
of nkStrLit..nkTripleStrLit:
|
|
putDataIntoDest(p, d, n, genLiteral(p, n))
|
|
of nkIntLit..nkUInt64Lit,
|
|
nkFloatLit..nkFloat128Lit, nkCharLit:
|
|
putIntoDest(p, d, n, genLiteral(p, n))
|
|
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
|
nkCallStrLit:
|
|
genLineDir(p, n) # may be redundant, it is generated in fixupCall as well
|
|
let op = n[0]
|
|
if n.typ.isNil:
|
|
# discard the value:
|
|
var a: TLoc
|
|
if op.kind == nkSym and op.sym.magic != mNone:
|
|
genMagicExpr(p, n, a, op.sym.magic)
|
|
else:
|
|
genCall(p, n, a)
|
|
else:
|
|
# load it into 'd':
|
|
if op.kind == nkSym and op.sym.magic != mNone:
|
|
genMagicExpr(p, n, d, op.sym.magic)
|
|
else:
|
|
genCall(p, n, d)
|
|
of nkCurly:
|
|
if isDeepConstExpr(n) and n.len != 0:
|
|
putIntoDest(p, d, n, genSetNode(p, n))
|
|
else:
|
|
genSetConstr(p, n, d)
|
|
of nkBracket:
|
|
if isDeepConstExpr(n) and n.len != 0:
|
|
exprComplexConst(p, n, d)
|
|
elif skipTypes(n.typ, abstractVarRange).kind == tySequence:
|
|
genSeqConstr(p, n, d)
|
|
else:
|
|
genArrayConstr(p, n, d)
|
|
of nkPar, nkTupleConstr:
|
|
if n.typ != nil and n.typ.kind == tyProc and n.len == 2:
|
|
genClosure(p, n, d)
|
|
elif isDeepConstExpr(n) and n.len != 0:
|
|
exprComplexConst(p, n, d)
|
|
else:
|
|
genTupleConstr(p, n, d)
|
|
of nkObjConstr: genObjConstr(p, n, d)
|
|
of nkCast: genCast(p, n, d)
|
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
|
|
of nkHiddenAddr, nkAddr: genAddr(p, n, d)
|
|
of nkBracketExpr: genBracketExpr(p, n, d)
|
|
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
|
|
of nkDotExpr: genRecordField(p, n, d)
|
|
of nkCheckedFieldExpr: genCheckedRecordField(p, n, d)
|
|
of nkBlockExpr, nkBlockStmt: genBlock(p, n, d)
|
|
of nkStmtListExpr: genStmtListExpr(p, n, d)
|
|
of nkStmtList: genStmtList(p, n)
|
|
of nkIfExpr, nkIfStmt: genIf(p, n, d)
|
|
of nkWhen:
|
|
# This should be a "when nimvm" node.
|
|
expr(p, n[1][0], d)
|
|
of nkObjDownConv: downConv(p, n, d)
|
|
of nkObjUpConv: upConv(p, n, d)
|
|
of nkChckRangeF: genRangeChck(p, n, d)
|
|
of nkChckRange64: genRangeChck(p, n, d)
|
|
of nkChckRange: genRangeChck(p, n, d)
|
|
of nkStringToCString: convStrToCStr(p, n, d)
|
|
of nkCStringToString: convCStrToStr(p, n, d)
|
|
of nkLambdaKinds:
|
|
var sym = n[namePos].sym
|
|
genProc(p.module, sym)
|
|
if sym.loc.r == nil or sym.loc.lode == nil:
|
|
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
|
|
putLocIntoDest(p, d, sym.loc)
|
|
of nkClosure: genClosure(p, n, d)
|
|
|
|
of nkEmpty: discard
|
|
of nkWhileStmt: genWhileStmt(p, n)
|
|
of nkVarSection, nkLetSection: genVarStmt(p, n)
|
|
of nkConstSection: discard # consts generated lazily on use
|
|
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
|
|
of nkCaseStmt: genCase(p, n, d)
|
|
of nkReturnStmt: genReturnStmt(p, n)
|
|
of nkBreakStmt: genBreakStmt(p, n)
|
|
of nkAsgn:
|
|
if nfPreventCg notin n.flags:
|
|
genAsgn(p, n, fastAsgn=false)
|
|
of nkFastAsgn:
|
|
if nfPreventCg notin n.flags:
|
|
# transf is overly aggressive with 'nkFastAsgn', so we work around here.
|
|
# See tests/run/tcnstseq3 for an example that would fail otherwise.
|
|
genAsgn(p, n, fastAsgn=p.prc != nil)
|
|
of nkDiscardStmt:
|
|
let ex = n[0]
|
|
if ex.kind != nkEmpty:
|
|
genLineDir(p, n)
|
|
var a: TLoc
|
|
initLocExprSingleUse(p, ex, a)
|
|
line(p, cpsStmts, "(void)(" & a.r & ");\L")
|
|
of nkAsmStmt: genAsmStmt(p, n)
|
|
of nkTryStmt, nkHiddenTryStmt:
|
|
case p.config.exc
|
|
of excGoto:
|
|
genTryGoto(p, n, d)
|
|
of excCpp:
|
|
genTryCpp(p, n, d)
|
|
else:
|
|
genTrySetjmp(p, n, d)
|
|
of nkRaiseStmt: genRaiseStmt(p, n)
|
|
of nkTypeSection:
|
|
# we have to emit the type information for object types here to support
|
|
# separate compilation:
|
|
genTypeSection(p.module, n)
|
|
of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
|
|
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
|
nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt:
|
|
discard
|
|
of nkPragma: genPragma(p, n)
|
|
of nkPragmaBlock: expr(p, n.lastSon, d)
|
|
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
|
|
if n[genericParamsPos].kind == nkEmpty:
|
|
var prc = n[namePos].sym
|
|
# due to a bug/limitation in the lambda lifting, unused inner procs
|
|
# are not transformed correctly. We work around this issue (#411) here
|
|
# by ensuring it's no inner proc (owner is a module):
|
|
if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
|
|
if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
|
|
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
|
|
(prc.kind == skMethod):
|
|
# Generate proc even if empty body, bugfix #11651.
|
|
genProc(p.module, prc)
|
|
of nkParForStmt: genParForStmt(p, n)
|
|
of nkState: genState(p, n)
|
|
of nkGotoState:
|
|
# simply never set it back to 0 here from here on...
|
|
inc p.splitDecls
|
|
genGotoState(p, n)
|
|
of nkBreakState: genBreakState(p, n, d)
|
|
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
|
|
|
proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope =
|
|
if n.kind == nkExprColonExpr: result = genBracedInit(p, n[1], isConst)
|
|
else: result = genBracedInit(p, n, isConst)
|
|
|
|
proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
|
|
var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc})
|
|
case t.kind
|
|
of tyBool: result = rope"NIM_FALSE"
|
|
of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0"
|
|
of tyFloat..tyFloat128: result = rope"0.0"
|
|
of tyCString, tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
|
|
tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil:
|
|
result = rope"NIM_NIL"
|
|
of tyString, tySequence:
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
result = rope"{0, NIM_NIL}"
|
|
else:
|
|
result = rope"NIM_NIL"
|
|
of tyProc:
|
|
if t.callConv != ccClosure:
|
|
result = rope"NIM_NIL"
|
|
else:
|
|
result = rope"{NIM_NIL, NIM_NIL}"
|
|
of tyObject:
|
|
var count = 0
|
|
result.add "{"
|
|
getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info)
|
|
result.add "}"
|
|
of tyTuple:
|
|
result = rope"{"
|
|
for i in 0..<t.len:
|
|
if i > 0: result.add ", "
|
|
result.add getDefaultValue(p, t[i], info)
|
|
result.add "}"
|
|
of tyArray:
|
|
result = rope"{"
|
|
for i in 0..<toInt(lengthOrd(p.config, t.sons[0])):
|
|
if i > 0: result.add ", "
|
|
result.add getDefaultValue(p, t.sons[1], info)
|
|
result.add "}"
|
|
#result = rope"{}"
|
|
of tySet:
|
|
if mapType(p.config, t) == ctArray: result = rope"{}"
|
|
else: result = rope"0"
|
|
else:
|
|
globalError(p.config, info, "cannot create null element for: " & $t.kind)
|
|
|
|
proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
|
result: var Rope; count: var int;
|
|
isConst: bool, info: TLineInfo) =
|
|
case obj.kind
|
|
of nkRecList:
|
|
for it in obj.sons:
|
|
getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
|
|
of nkRecCase:
|
|
getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
|
|
if count > 0: result.add ", "
|
|
var branch = Zero
|
|
if constOrNil != nil:
|
|
## find kind value, default is zero if not specified
|
|
for i in 1..<constOrNil.len:
|
|
if constOrNil[i].kind == nkExprColonExpr:
|
|
if constOrNil[i][0].sym.name.id == obj[0].sym.name.id:
|
|
branch = getOrdValue(constOrNil[i][1])
|
|
break
|
|
elif i == obj[0].sym.position:
|
|
branch = getOrdValue(constOrNil[i])
|
|
break
|
|
|
|
var selectedBranch = -1
|
|
block branchSelection:
|
|
for i in 1 ..< obj.len:
|
|
for j in 0 .. obj[i].len - 2:
|
|
if obj[i][j].kind == nkRange:
|
|
let x = getOrdValue(obj[i][j][0])
|
|
let y = getOrdValue(obj[i][j][1])
|
|
if branch >= x and branch <= y:
|
|
selectedBranch = i
|
|
break branchSelection
|
|
elif getOrdValue(obj[i][j]) == branch:
|
|
selectedBranch = i
|
|
break branchSelection
|
|
if obj[i].len == 1:
|
|
# else branch
|
|
selectedBranch = i
|
|
assert(selectedBranch >= 1)
|
|
|
|
result.add "{"
|
|
var countB = 0
|
|
let b = lastSon(obj[selectedBranch])
|
|
# designated initilization is the only way to init non first element of unions
|
|
# branches are allowed to have no members (b.len == 0), in this case they don't need initializer
|
|
if b.kind == nkRecList and b.len > 0:
|
|
result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
|
|
getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info)
|
|
result.add "}"
|
|
elif b.kind == nkSym:
|
|
result.add "." & mangleRecFieldName(p.module, b.sym) & " = "
|
|
getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info)
|
|
result.add "}"
|
|
|
|
of nkSym:
|
|
if count > 0: result.add ", "
|
|
inc count
|
|
let field = obj.sym
|
|
if constOrNil != nil:
|
|
for i in 1..<constOrNil.len:
|
|
if constOrNil[i].kind == nkExprColonExpr:
|
|
if constOrNil[i][0].sym.name.id == field.name.id:
|
|
result.add genBracedInit(p, constOrNil[i][1], isConst)
|
|
return
|
|
elif i == field.position:
|
|
result.add genBracedInit(p, constOrNil[i], isConst)
|
|
return
|
|
# not found, produce default value:
|
|
result.add getDefaultValue(p, field.typ, info)
|
|
else:
|
|
localError(p.config, info, "cannot create null element for: " & $obj)
|
|
|
|
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
|
|
result: var Rope; count: var int;
|
|
isConst: bool, info: TLineInfo) =
|
|
var base = t[0]
|
|
let oldRes = result
|
|
let oldcount = count
|
|
if base != nil:
|
|
result.add "{"
|
|
base = skipTypes(base, skipPtrs)
|
|
getNullValueAuxT(p, orig, base, base.n, constOrNil, result, count, isConst, info)
|
|
result.add "}"
|
|
elif not isObjLackingTypeField(t):
|
|
result.add genTypeInfo(p.module, orig, obj.info)
|
|
inc count
|
|
getNullValueAux(p, t, obj, constOrNil, result, count, isConst, info)
|
|
# do not emit '{}' as that is not valid C:
|
|
if oldcount == count: result = oldRes
|
|
|
|
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool): Rope =
|
|
result = nil
|
|
let t = n.typ.skipTypes(abstractInstOwned)
|
|
var count = 0
|
|
#if not isObjLackingTypeField(t) and not p.module.compileToCpp:
|
|
# result.addf("{$1}", [genTypeInfo(p.module, t)])
|
|
# inc count
|
|
if t.kind == tyObject:
|
|
getNullValueAuxT(p, t, t, t.n, n, result, count, isConst, n.info)
|
|
result = "{$1}$n" % [result]
|
|
|
|
proc genConstSimpleList(p: BProc, n: PNode; isConst: bool): Rope =
|
|
result = rope("{")
|
|
for i in 0..<n.len - 1:
|
|
result.addf("$1,$n", [genNamedConstExpr(p, n[i], isConst)])
|
|
if n.len > 0:
|
|
result.add(genNamedConstExpr(p, n[^1], isConst))
|
|
result.addf("}$n", [])
|
|
|
|
proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope =
|
|
var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope]
|
|
if n.len > 0:
|
|
# array part needs extra curlies:
|
|
data.add(", {")
|
|
for i in 0..<n.len:
|
|
if i > 0: data.addf(",$n", [])
|
|
data.add genBracedInit(p, n[i], isConst)
|
|
data.add("}")
|
|
data.add("}")
|
|
|
|
result = getTempName(p.module)
|
|
let base = t.skipTypes(abstractInst)[0]
|
|
|
|
appcg(p.module, cfsData,
|
|
"static $5 struct {$n" &
|
|
" #TGenericSeq Sup;$n" &
|
|
" $1 data[$2];$n" &
|
|
"} $3 = $4;$n", [
|
|
getTypeDesc(p.module, base), n.len, result, data,
|
|
if isConst: "NIM_CONST" else: ""])
|
|
|
|
result = "(($1)&$2)" % [getTypeDesc(p.module, t), result]
|
|
|
|
proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope =
|
|
var data = rope"{"
|
|
for i in 0..<n.len:
|
|
if i > 0: data.addf(",$n", [])
|
|
data.add genBracedInit(p, n[i], isConst)
|
|
data.add("}")
|
|
|
|
let payload = getTempName(p.module)
|
|
let base = t.skipTypes(abstractInst)[0]
|
|
|
|
appcg(p.module, cfsData,
|
|
"static $5 struct {$n" &
|
|
" NI cap; $1 data[$2];$n" &
|
|
"} $3 = {$2 | NIM_STRLIT_FLAG, $4};$n", [
|
|
getTypeDesc(p.module, base), n.len, payload, data,
|
|
if isConst: "const" else: ""])
|
|
result = "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload]
|
|
|
|
proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope =
|
|
case n.kind
|
|
of nkHiddenStdConv, nkHiddenSubConv:
|
|
result = genBracedInit(p, n[1], isConst)
|
|
else:
|
|
var ty = tyNone
|
|
if n.typ == nil:
|
|
if n.kind in nkStrKinds:
|
|
ty = tyString
|
|
else:
|
|
internalError(p.config, n.info, "node has no type")
|
|
else:
|
|
ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind
|
|
case ty
|
|
of tySet:
|
|
var cs: TBitSet
|
|
toBitSet(p.config, n, cs)
|
|
result = genRawSetData(cs, int(getSize(p.config, n.typ)))
|
|
of tySequence:
|
|
if optSeqDestructors in p.config.globalOptions:
|
|
result = genConstSeqV2(p, n, n.typ, isConst)
|
|
else:
|
|
result = genConstSeq(p, n, n.typ, isConst)
|
|
of tyProc:
|
|
if n.typ.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit:
|
|
# Conversion: nimcall -> closure.
|
|
# this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
|
|
# this behaviour is needed since closure_var = nil must be
|
|
# expanded to {NIM_NIL,NIM_NIL}
|
|
# in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
|
|
# leading to duplicate code like this:
|
|
# "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
|
|
if n[0].kind == nkNilLit:
|
|
result = ~"{NIM_NIL,NIM_NIL}"
|
|
else:
|
|
var d: TLoc
|
|
initLocExpr(p, n[0], d)
|
|
result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, n.typ, clHalfWithEnv), rdLoc(d)]
|
|
else:
|
|
var d: TLoc
|
|
initLocExpr(p, n, d)
|
|
result = rdLoc(d)
|
|
of tyArray, tyTuple, tyOpenArray, tyVarargs:
|
|
result = genConstSimpleList(p, n, isConst)
|
|
of tyObject:
|
|
result = genConstObjConstr(p, n, isConst)
|
|
of tyString, tyCString:
|
|
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
|
|
result = genStringLiteralV2Const(p.module, n, isConst)
|
|
else:
|
|
var d: TLoc
|
|
initLocExpr(p, n, d)
|
|
result = rdLoc(d)
|
|
else:
|
|
var d: TLoc
|
|
initLocExpr(p, n, d)
|
|
result = rdLoc(d)
|