Skip to content

cast now works in VM in more contexts, behaving like in RT; eg: int|pointer <=> ptr T | ref T (eg: enables nimyaml at CT) #15947

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

Closed
wants to merge 7 commits into from
Closed
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
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@

- VM now supports `addr(mystring[ind])` (index + index assignment)

- `cast` now works in VM in more contexts, behaving like in RT; eg: `int <=> ptr T | ref T`,
see `tcastint.testCastRefOrPtr`
- `cast` now works in VM in more contexts, behaving the same as in runtime, in particular
`cast[int](x)` now works for `x: ptr T | ref T`, and likewise for reverse direction.

## Tool changes

Expand Down
73 changes: 51 additions & 22 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ 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 =
proc derefPtrToReg(c: PCtx, tos: PStackFrame, pc: int, 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:
Expand Down Expand Up @@ -146,7 +146,19 @@ proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: b
of tyFloat: fun(floatVal, float, rkFloat)
of tyFloat32: fun(floatVal, float32, rkFloat)
of tyFloat64: fun(floatVal, float64, rkFloat)
else: return false
of tyObject:
if isAssign:
let lhs = cast[PNode](address)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait what?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same as all the other pre-existing cast[ptr T](address)[] in derefPtrToReg, with T = TNode in this case.
The tests exercise all of this, and make CT behavior consistent with RT behavior.

let rhs = r.node
if lhs.len != rhs.len: internalError(c.config, c.debug[pc], "derefPtrToReg failed")
for i in 0..<lhs.len:
lhs[i] = copyTree(rhs[i])
else:
r.ensureKind(rkNode)
r.node = cast[PNode](address)
return true
else:
return false

proc createStrKeepNode(x: var TFullReg; keepNode=true) =
if x.node.isNil or not keepNode:
Expand Down Expand Up @@ -499,8 +511,7 @@ const
"if you are sure this is not a bug in your code, compile with `--maxLoopIterationsVM:number` (current value: $1)"
errFieldXNotFound = "node lacks field: "


template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool =
template maybeHandlePtr(c: PCtx, tos: PStackFrame, pc: int, node2: PNode, reg: TFullReg, isAssign2: bool): bool =
let node = node2 # prevent double evaluation
if node.kind == nkNilLit:
stackTrace(c, tos, pc, errNilAccess)
Expand All @@ -509,10 +520,10 @@ template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool =
assert node.kind == nkIntLit, $(node.kind)
assert typ != nil
let typ2 = if typ.kind == tyPtr: typ[0] else: typ
if not derefPtrToReg(node.intVal, typ2, reg, isAssign = isAssign2):
# tyObject not supported in this context
if derefPtrToReg(c, tos, pc, node.intVal, typ2, reg, isAssign = isAssign2): true
else:
stackTrace(c, tos, pc, "deref unsupported ptr type: " & $(typeToString(typ), typ.kind))
true
false
else:
false

Expand Down Expand Up @@ -597,9 +608,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of 1: # PtrLikeKinds
case regs[rb].kind
of rkNode:
regs[ra].intVal = cast[int](regs[rb].node.intVal)
case regs[rb].node.kind
of nkObjConstr:
regs[ra].intVal = cast[int](regs[rb].node)
of nkIntLit:
regs[ra].intVal = cast[int](regs[rb].node.intVal)
else:
stackTrace(c, tos, pc, "opcCastPtrToInt: " & $regs[rb].node.kind)
of rkNodeAddr:
regs[ra].intVal = cast[int](regs[rb].nodeAddr)
of rkInt:
# could be tightened by checking type, eg `tyPointer`
regs[ra].intVal = regs[rb].intVal
else:
stackTrace(c, tos, pc, "opcCastPtrToInt: got " & $regs[rb].kind)
of 2: # tyRef
Expand All @@ -608,15 +628,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcCastIntToPtr:
let rb = instr.regB
let typ = regs[ra].node.typ
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
if typ.kind == tyRef:
template fn(val) = regs[ra].node = cast[PNode](val.intVal)
case regs[rb].kind
of rkInt: fn(regs[rb])
of rkNode: fn(regs[rb].node)
else: stackTrace(c, tos, pc, "regs[rb].kind: " & $regs[rb].kind)
else:
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 @@ -717,7 +744,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
case src.kind
of nkEmpty..nkNilLit:
# 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)
# derefPtrToReg(c, tos, pc, 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:
Expand Down Expand Up @@ -786,8 +813,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of rkNode:
if regs[rb].node.kind == nkRefTy:
regs[ra].node = regs[rb].node[0]
elif not maybeHandlePtr(regs[rb].node, regs[ra], false):
## e.g.: typ.kind = tyObject
elif not maybeHandlePtr(c, tos, pc, regs[rb].node, regs[ra], false):
# e.g.: typ.kind = tyObject
ensureKind(rkNode)
regs[ra].node = regs[rb].node
else:
Expand All @@ -811,7 +838,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
of rkNode:
# xxx: also check for nkRefTy as in opcLdDeref?
if not maybeHandlePtr(regs[ra].node, regs[rc], true):
if not maybeHandlePtr(c, tos, pc, regs[ra].node, regs[rc], true):
regs[ra].node[] = regs[rc].regToNode[]
regs[ra].node.flags.incl nfIsRef
else: stackTrace(c, tos, pc, errNilAccess)
Expand Down Expand Up @@ -1012,6 +1039,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
ret = ptrEquality(regs[rb].nodeAddr, regs[rc].node)
elif regs[rc].kind == rkNodeAddr:
ret = ptrEquality(regs[rc].nodeAddr, regs[rb].node)
elif regs[rc].kind == rkInt and regs[rb].kind == rkInt:
ret = regs[rb].intVal == regs[rc].intVal
else:
let nb = regs[rb].node
let nc = regs[rc].node
Expand Down Expand Up @@ -1433,7 +1462,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
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):
elif not derefPtrToReg(c, tos, pc, 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
Expand Down
5 changes: 3 additions & 2 deletions compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -889,13 +889,14 @@ 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 in PtrLikeKinds + {tyRef} and dst.kind == tyInt:
elif src.kind in PtrLikeKinds + {tyRef} and dst.kind in {tyInt, tyPointer}:
let tmp = c.genx(n[1])
if dest < 0: dest = c.getTemp(n[0].typ)
var imm: BiggestInt = if src.kind in PtrLikeKinds: 1 else: 2
c.gABI(n, opcCastPtrToInt, dest, tmp, imm)
c.freeTemp(tmp)
elif src.kind in PtrLikeKinds + {tyInt} and dst.kind in PtrLikeKinds:
elif (src.kind in PtrLikeKinds and dst.kind in PtrLikeKinds) or
(src.kind in {tyInt, tyPointer} and dst.kind in PtrLikeKinds + {tyRef}):
let tmp = c.genx(n[1])
if dest < 0: dest = c.getTemp(n[0].typ)
c.gABx(n, opcSetType, dest, c.genType(dst))
Expand Down
49 changes: 38 additions & 11 deletions tests/vm/tcastint.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
discard """
output: "OK"
"""

import macros

type
Expand Down Expand Up @@ -292,16 +288,47 @@ proc test_float32_castB() =
# any surprising content.
doAssert cast[uint64](c) == 3270918144'u64

test()
test_float_cast()
test_float32_cast()
free_integer_casting()
test_float32_castB()
static:
proc testCastRefOrPtr() =
type TFoo = object
f0: string
template fnAux(Lhs) =
block:
template fn(Foo, a) =
let pa = cast[Lhs](a)
var a2 = cast[Foo](pa)
let pa2 = cast[Lhs](pa)
doAssert a2[] == a[]
doAssert a2.f0 == a.f0
doAssert pa2 == pa

a2.f0 = "abc2"
doAssert a.f0 == "abc2"
let b = TFoo(f0: "abc3")
a2[] = b
doAssert a2.f0 == "abc3"
doAssert a2[] == b

block: # ref <=> Lhs
type Foo = ref TFoo
var a = Foo(f0: "abc")
fn(Foo, a)

block: # ptr <=> Lhs
type Foo = ptr TFoo
var a0 = TFoo(f0: "abc")
let a = a0.addr
fn(Foo, a)

fnAux(int)
fnAux(pointer)

template main =
test()
test_float_cast()
test_float32_cast()
free_integer_casting()
test_float32_castB()
testCastRefOrPtr()

echo "OK"
static: main()
main()