Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VM: support importc var, ptr/pointer types, cast int <=> ptr/pointer #12877

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ type
nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM
nfIsPtr # this node is a 'ptr' node; used for the VM
nfPreventCg # this node should be ignored by the codegen
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
nfFromTemplate # a top-level node returned from a template
Expand Down Expand Up @@ -856,6 +857,9 @@ type
loc*: TLoc
annex*: PLib # additional fields (seldom used, so we use a
# reference to another object to save space)
when hasFFI:
cname*: string # resolved C declaration name in importc decl, eg:
# proc fun() {.importc: "$1aux".} => cname = funaux
constraint*: PNode # additional constraints like 'lit|result'; also
# misused for the codegenDecl pragma in the hope
# it won't cause problems
Expand Down Expand Up @@ -978,12 +982,13 @@ const
tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
tyProc, tyError}
PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField,
nfIsRef, nfPreventCg, nfLL,
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
nfFromTemplate, nfDefaultRefsParam,
nfExecuteOnReload}
namePos* = 0
Expand Down
2 changes: 1 addition & 1 deletion compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =

proc mangleDynLibProc(sym: PSym): Rope =
# we have to build this as a single rope in order not to trip the
# optimization in genInfixCall
# optimization in genInfixCall, see test tests/cpp/t8241.nim
if sfCompilerProc in sym.flags:
# NOTE: sym.loc.r is the external name!
result = rope(sym.name.s)
Expand Down
13 changes: 3 additions & 10 deletions compiler/evalffi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,12 @@ proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo)
const
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon

var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable

proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
let name = $sym.loc.r
let name = sym.cname # $sym.loc.r would point to internal name
# the AST does not support untyped pointers directly, so we use an nkIntLit
# that contains the address instead:
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
case name
of "stdin": result.intVal = cast[ByteAddress](system.stdin)
of "stdout": result.intVal = cast[ByteAddress](system.stdout)
of "stderr": result.intVal = cast[ByteAddress](system.stderr)
of "vmErrnoWrapper": result.intVal = cast[ByteAddress](myerrno)
else:
when true:
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
globalError(conf, sym.info, "dynlib needs to be a string lit")
Expand All @@ -74,7 +67,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
let dllhandle = getDll(conf, gDllCache, dll, sym.info)
theAddr = dllhandle.symAddr(name)
if theAddr.isNil: globalError(conf, sym.info, "cannot import: " & sym.name.s)
if theAddr.isNil: globalError(conf, sym.info, "cannot import: " & name)
result.intVal = cast[ByteAddress](theAddr)

proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.TType =
Expand Down
2 changes: 2 additions & 0 deletions compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
s.loc.r = rope(extname % s.name.s)
except ValueError:
localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
when hasFFI:
s.cname = $s.loc.r
if c.config.cmd == cmdPretty and '$' notin extname:
# note that '{.importc.}' is transformed into '{.importc: "$1".}'
s.loc.flags.incl(lfFullExternalName)
Expand Down
172 changes: 153 additions & 19 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,40 @@ template decodeBx(k: untyped) {.dirty.} =
template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b)
# XXX fix minor 'shallowCopy' overloading bug in compiler

proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: bool): bool =
# nim bug: `isAssign: static bool` doesn't work, giving odd compiler error
template fun(field, T, rkind) =
if isAssign:
cast[ptr T](address)[] = T(r.field)
else:
# ensureKind(rkind)
if r.kind != rkind:
myreset(r)
r.kind = rkind
let val = cast[ptr T](address)[]
when T is SomeInteger:
r.field = BiggestInt(val)
else:
r.field = val
return true

## see also typeinfo.getBiggestInt
case typ.kind
of tyInt: fun(intVal, int, rkInt)
of tyInt8: fun(intVal, int8, rkInt)
of tyInt16: fun(intVal, int16, rkInt)
of tyInt32: fun(intVal, int32, rkInt)
of tyInt64: fun(intVal, int64, rkInt)
of tyUInt: fun(intVal, uint, rkInt)
of tyUInt8: fun(intVal, uint8, rkInt)
of tyUInt16: fun(intVal, uint16, rkInt)
of tyUInt32: fun(intVal, uint32, rkInt)
of tyUInt64: fun(intVal, uint64, rkInt) # note: differs from typeinfo.getBiggestInt
of tyFloat: fun(floatVal, float, rkFloat)
of tyFloat32: fun(floatVal, float32, rkFloat)
of tyFloat64: fun(floatVal, float64, rkFloat)
else: return false

proc createStrKeepNode(x: var TFullReg; keepNode=true) =
if x.node.isNil or not keepNode:
x.node = newNode(nkStrLit)
Expand Down Expand Up @@ -242,14 +276,20 @@ proc writeField(n: var PNode, x: TFullReg) =
of rkNodeAddr: n = x.nodeAddr[]

proc putIntoReg(dest: var TFullReg; n: PNode) =
template funInt() =
dest.kind = rkInt
dest.intVal = n.intVal
case n.kind
of nkStrLit..nkTripleStrLit:
dest.kind = rkNode
createStr(dest)
dest.node.strVal = n.strVal
of nkCharLit..nkUInt64Lit:
dest.kind = rkInt
dest.intVal = n.intVal
of nkIntLit: # use `nkPtrLit` once this is added
if dest.kind == rkNode: dest.node = n
elif n.typ != nil and n.typ.kind in PtrLikeKinds:
dest = TFullReg(kind: rkNode, node: n)
else: funInt()
of {nkCharLit..nkUInt64Lit} - {nkIntLit}: funInt()
of nkFloatLit..nkFloat128Lit:
dest.kind = rkFloat
dest.floatVal = n.floatVal
Expand Down Expand Up @@ -567,6 +607,33 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rb = instr.regB
ensureKind(rkFloat)
regs[ra].floatVal = cast[float64](int64(regs[rb].intVal))

of opcCastPtrToInt: # RENAME opcCastPtrOrRefToInt
decodeBImm(rkInt)
case imm
of 1: # PtrLikeKinds
case regs[rb].kind
of rkNode:
regs[ra].intVal = cast[int](regs[rb].node.intVal)
of rkNodeAddr:
regs[ra].intVal = cast[int](regs[rb].nodeAddr)
else:
stackTrace(c, tos, pc, "opcCastPtrToInt: got " & $regs[rb].kind)
of 2: # tyRef
regs[ra].intVal = cast[int](regs[rb].node)
else: assert false, $imm
of opcCastIntToPtr:
let rb = instr.regB
let typ = regs[ra].node.typ
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
let node2 = newNodeIT(nkIntLit, c.debug[pc], typ)
case regs[rb].kind
of rkInt: node2.intVal = regs[rb].intVal
of rkNode:
if regs[rb].node.typ.kind notin PtrLikeKinds:
stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].node.typ: " & $regs[rb].node.typ.kind)
node2.intVal = regs[rb].node.intVal
else: stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].kind: " & $regs[rb].kind)
regs[ra].node = node2
of opcAsgnComplex:
asgnComplex(regs[ra], regs[instr.regB])
of opcFastAsgnComplex:
Expand Down Expand Up @@ -651,7 +718,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let src = regs[rb].node
case src.kind
of nkEmpty..nkNilLit:
stackTrace(c, tos, pc, errNilAccess)
# for nkPtrLit, this could be supported in the future, use something like:
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
# where we compute the offset in bytes for field rc
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
of nkObjConstr:
let n = src[rc + 1].skipColon
regs[ra].node = n
Expand Down Expand Up @@ -718,10 +788,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if regs[rb].node.kind == nkRefTy:
regs[ra].node = regs[rb].node[0]
else:
ensureKind(rkNode)
regs[ra].node = regs[rb].node
let node = regs[rb].node
let typ = node.typ
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
# see also `nfIsPtr`
if node.kind == nkIntLit:
var typ2 = typ
doAssert typ != nil
if typ.kind == tyPtr:
typ2 = typ2[0]
if not derefPtrToReg(node.intVal, typ2, regs[ra], isAssign = false):
# tyObject not supported in this context
stackTrace(c, tos, pc, "opcLdDeref unsupported ptr type: " & $(typeToString(typ), typ.kind))
else:
## eg: typ.kind = tyObject
ensureKind(rkNode)
regs[ra].node = regs[rb].node
else:
stackTrace(c, tos, pc, errNilAccess)
stackTrace(c, tos, pc, errNilAccess & " kind: " & $regs[rb].kind)
of opcWrDeref:
# a[] = c; b unused
let ra = instr.regA
Expand All @@ -742,8 +825,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of rkNode:
if regs[ra].node.kind == nkNilLit:
stackTrace(c, tos, pc, errNilAccess)
regs[ra].node[] = regs[rc].regToNode[]
regs[ra].node.flags.incl nfIsRef
let node = regs[ra].node
let typ = node.typ
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
if nfIsPtr in node.flags or (typ != nil and typ.kind == tyPtr):
assert node.kind == nkIntLit, $(node.kind)
var typ2 = typ
if typ.kind == tyPtr:
typ2 = typ2[0]
if not derefPtrToReg(node.intVal, typ2, regs[rc], isAssign = true):
stackTrace(c, tos, pc, "opcWrDeref unsupported ptr type: " & $(typeToString(typ), typ.kind))
else:
regs[ra].node[] = regs[rc].regToNode[]
regs[ra].node.flags.incl nfIsRef
else: stackTrace(c, tos, pc, errNilAccess)
of opcAddInt:
decodeBC(rkInt)
Expand Down Expand Up @@ -918,22 +1011,40 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
of opcEqRef:
var ret = false
decodeBC(rkInt)
template getTyp(n): untyped =
n.typ.skipTypes(abstractInst)
proc ptrEquality(n1: ptr PNode, n2: PNode): bool =
## true if n2.intVal represents a ptr equal to n1
let p1 = cast[int](n1)
case n2.kind
of nkNilLit: return p1 == 0
of nkIntLit: # TODO: nkPtrLit
# for example, n1.kind == nkFloatLit (ptr float)
# the problem is that n1.typ == nil so we can't compare n1.typ and n2.typ
# this is the best we can do (pending making sure we assign a valid n1.typ to nodeAddr's)
let t2 = n2.getTyp
return t2.kind in PtrLikeKinds and n2.intVal == p1
else: return false

if regs[rb].kind == rkNodeAddr:
if regs[rc].kind == rkNodeAddr:
regs[ra].intVal = ord(regs[rb].nodeAddr == regs[rc].nodeAddr)
ret = regs[rb].nodeAddr == regs[rc].nodeAddr
else:
assert regs[rc].kind == rkNode
# we know these cannot be equal
regs[ra].intVal = ord(false)
ret = ptrEquality(regs[rb].nodeAddr, regs[rc].node)
elif regs[rc].kind == rkNodeAddr:
assert regs[rb].kind == rkNode
# we know these cannot be equal
regs[ra].intVal = ord(false)
ret = ptrEquality(regs[rc].nodeAddr, regs[rb].node)
else:
regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and
regs[rc].node.kind == nkNilLit) or
regs[rb].node == regs[rc].node)
let nb = regs[rb].node
let nc = regs[rc].node
if nb.kind != nc.kind: discard
elif (nb == nc) or (nb.kind == nkNilLit): ret = true
elif nb.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit
let tb = nb.getTyp
let tc = nc.getTyp
ret = tb.kind in PtrLikeKinds and tc.kind == tb.kind
regs[ra].intVal = ord(ret)
of opcEqNimNode:
decodeBC(rkInt)
regs[ra].intVal =
Expand Down Expand Up @@ -1338,6 +1449,29 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rb = instr.regBx - wordExcess - 1
ensureKind(rkNode)
regs[ra].node = c.globals[rb]
of opcLdGlobalDerefFFI:
let rb = instr.regBx - wordExcess - 1
let node = c.globals[rb]
let typ = node.typ
doAssert node.kind == nkIntLit, $(node.kind)
if typ.kind == tyPtr:
ensureKind(rkNode)
# use nkPtrLit once this is added
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
let node2 = newNodeIT(nkIntLit, c.debug[pc], typ)
node2.intVal = cast[ptr int](node.intVal)[]
node2.flags.incl nfIsPtr
regs[ra].node = node2
elif not derefPtrToReg(node.intVal, typ, regs[ra], isAssign = false):
stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ[0].kind))
of opcLdGlobalAddrDerefFFI:
let rb = instr.regBx - wordExcess - 1
let node = c.globals[rb]
let typ = node.typ
var node2 = newNodeIT(nkIntLit, node.info, typ)
node2.intVal = node.intVal
node2.flags.incl nfIsPtr
ensureKind(rkNode)
regs[ra].node = node2
of opcLdGlobalAddr:
let rb = instr.regBx - wordExcess - 1
ensureKind(rkNodeAddr)
Expand Down
4 changes: 4 additions & 0 deletions compiler/vmdef.nim
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ type
opcCastIntToFloat64, # int and float must be of the same byte size
opcCastFloatToInt32, # int and float must be of the same byte size
opcCastFloatToInt64, # int and float must be of the same byte size
opcCastPtrToInt,
opcCastIntToPtr,
opcFastAsgnComplex,
opcNodeToReg,

Expand Down Expand Up @@ -169,6 +171,8 @@ type
opcAsgnConst, # dest = copy(constants[Bx])
opcLdGlobal, # dest = globals[Bx]
opcLdGlobalAddr, # dest = addr(globals[Bx])
opcLdGlobalDerefFFI, # dest = globals[Bx][]
opcLdGlobalAddrDerefFFI, # globals[Bx][] = ...

opcLdImmInt, # dest = immediate value
opcNBindSym, opcNDynBindSym,
Expand Down
Loading