Skip to content

Commit

Permalink
VM: allow all importc var, cast[int](ptr)
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Dec 11, 2019
1 parent e4e18b4 commit 928e4e6
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 19 deletions.
2 changes: 2 additions & 0 deletions compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,8 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
if lib.name == nil: internalError(m.config, "loadDynamicLib")

proc mangleDynLibProc(sym: PSym): Rope =
if sym.loc.r != nil:
return sym.loc.r # should that be `rope($sym.loc.r)`? (see comment below)
# we have to build this as a single rope in order not to trip the
# optimization in genInfixCall
if sfCompilerProc in sym.flags:
Expand Down
11 changes: 2 additions & 9 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
# 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
99 changes: 93 additions & 6 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) # checkme: 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,6 +276,11 @@ proc writeField(n: var PNode, x: TFullReg) =
of rkNodeAddr: n = x.nodeAddr[]

proc putIntoReg(dest: var TFullReg; n: PNode) =
if dest.kind == rkNode and n.kind == nkIntLit:
# use `nkPtrLit` once this becomes a thing
dest.node = n
return

case n.kind
of nkStrLit..nkTripleStrLit:
dest.kind = rkNode
Expand Down Expand Up @@ -567,6 +606,12 @@ 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:
let rb = instr.regB
ensureKind(rkInt)
if regs[rb].kind != rkNode:
stackTrace(c, tos, pc, "opcCastPtrToInt: regs[rb].kind: " & $regs[rb].kind)
regs[ra].intVal = cast[int](regs[rb].node.intVal)
of opcAsgnComplex:
asgnComplex(regs[ra], regs[instr.regB])
of opcFastAsgnComplex:
Expand Down Expand Up @@ -651,7 +696,9 @@ 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)
# TODO: for nkPtrLit, 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 +765,21 @@ 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
if node.kind == nkIntLit:
var typ2 = typ
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 +800,17 @@ 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
if node.kind == nkIntLit: # IMPROVE: use a flag?
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 @@ -1338,6 +1405,26 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rb = instr.regBx - wordExcess - 1
ensureKind(rkNode)
regs[ra].node = c.globals[rb]
of opcLdGlobalDeref:
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)
# checkme: nkPtrTy ? nkPtrLit?
regs[ra].node = newNodeIT(nkIntLit, node.info, typ)
regs[ra].node.intVal = cast[ptr int](node.intVal)[]
elif not derefPtrToReg(node.intVal, typ, regs[ra], isAssign = false):
stackTrace(c, tos, pc, "opcLdDeref unsupported ptr type: " & $(typeToString(typ), typ[0].kind))
of opcLdGlobalAddrDeref:
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
ensureKind(rkNode)
regs[ra].node = node2
of opcLdGlobalAddr:
let rb = instr.regBx - wordExcess - 1
ensureKind(rkNodeAddr)
Expand Down
3 changes: 3 additions & 0 deletions compiler/vmdef.nim
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ 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,
opcFastAsgnComplex,
opcNodeToReg,

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

opcLdImmInt, # dest = immediate value
opcNBindSym, opcNDynBindSym,
Expand Down
20 changes: 16 additions & 4 deletions compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,13 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcCastFloatToInt64, dest, tmp)
# narrowing for 64 bits not needed (no extended sign bits available).
c.freeTemp(tmp)
elif src.kind == tyPtr and dst.kind == tyInt:
let tmp = c.genx(n[1])
if dest < 0: dest = c.getTemp(n[0].typ)
c.gABC(n, opcCastPtrToInt, dest, tmp)
c.freeTemp(tmp)
else:
globalError(c.config, n.info, "VM is only allowed to 'cast' between integers and/or floats of same size")
globalError(c.config, n.info, "VM is only allowed to 'cast' between integers and/or floats of same size " & $(src.kind, dst.kind))

proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
unused(c, n, dest)
Expand Down Expand Up @@ -1624,17 +1629,24 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
assert card(flags * {gfNodeAddr, gfNode}) < 2
let s = n.sym
if s.isGlobal:
if sfCompileTime in s.flags or c.mode == emRepl or importcCondVar(s):
let isImportcVar = importcCondVar(s)
if sfCompileTime in s.flags or c.mode == emRepl or isImportcVar:
discard
elif s.position == 0:
cannotEval(c, n)
if s.position == 0:
if importcCond(s) or importcCondVar(s): c.importcSym(n.info, s)
if importcCond(s) or isImportcVar: c.importcSym(n.info, s)
else: genGlobalInit(c, n, s)
if dest < 0: dest = c.getTemp(n.typ)
assert s.typ != nil

if gfNodeAddr in flags:
c.gABx(n, opcLdGlobalAddr, dest, s.position)
if isImportcVar:
c.gABx(n, opcLdGlobalAddrDeref, dest, s.position)
else:
c.gABx(n, opcLdGlobalAddr, dest, s.position)
elif isImportcVar:
c.gABx(n, opcLdGlobalDeref, dest, s.position)
elif fitsRegister(s.typ) and gfNode notin flags:
var cc = c.getTemp(n.typ)
c.gABx(n, opcLdGlobal, cc, s.position)
Expand Down

0 comments on commit 928e4e6

Please sign in to comment.