Skip to content

Commit 2292ff9

Browse files
beef331narimiran
authored andcommitted
Implemented mSlice on the VM allowing toOpenArray to work at compile time. (#20586)
* Implemented opcSlice to make 'toOpenArray' work on the VM * Added nkOpenArray for VM to reduce bodgeness * Fixed range issues and erraneous comments * Range check correctly for openArrays in opcLdArr * Inverted logic for ldArr checking * vm now supports slicing strings * Added string tests * Removed usage of 'nkOpenArray' and redundant operations * Refactored vmSlice implementation, removing redundant and incorrect code * Made tuples go throw opcWrObj for field assignment * All strkinds should be considered for openarrays (cherry picked from commit 4aa67ad)
1 parent c5d62bc commit 2292ff9

File tree

4 files changed

+204
-30
lines changed

4 files changed

+204
-30
lines changed

compiler/vm.nim

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,15 @@ template takeAddress(reg, source) =
525525
reg.nodeAddr = addr source
526526
GC_ref source
527527

528+
proc takeCharAddress(c: PCtx, src: PNode, index: BiggestInt, pc: int): TFullReg =
529+
let typ = newType(tyPtr, nextTypeId c.idgen, c.module.owner)
530+
typ.add getSysType(c.graph, c.debug[pc], tyChar)
531+
var node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
532+
node.intVal = cast[int](src.strVal[index].addr)
533+
node.flags.incl nfIsPtr
534+
TFullReg(kind: rkNode, node: node)
535+
536+
528537
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
529538
var pc = start
530539
var tos = tos
@@ -658,14 +667,73 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
658667
else:
659668
ensureKind(rkNode)
660669
regs[ra].node = nb
670+
of opcSlice:
671+
# A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
672+
# nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
673+
decodeBC(rkNode)
674+
let
675+
collection = regs[ra].node
676+
leftInd = regs[rb].intVal
677+
rightInd = regs[rc].intVal
678+
679+
proc rangeCheck(left, right: BiggestInt, safeLen: BiggestInt) =
680+
if left < 0:
681+
stackTrace(c, tos, pc, formatErrorIndexBound(left, safeLen))
682+
683+
if right > safeLen:
684+
stackTrace(c, tos, pc, formatErrorIndexBound(right, safeLen))
685+
686+
case collection.kind
687+
of nkTupleConstr: # slice of a slice
688+
let safeLen = collection[2].intVal - collection[1].intVal
689+
rangeCheck(leftInd, rightInd, safeLen)
690+
let
691+
leftInd = leftInd + collection[1].intVal # Slice is from the start of the old
692+
rightInd = rightInd + collection[1].intVal
693+
694+
regs[ra].node = newTree(
695+
nkTupleConstr,
696+
collection[0],
697+
newIntNode(nkIntLit, BiggestInt leftInd),
698+
newIntNode(nkIntLit, BiggestInt rightInd)
699+
)
700+
701+
else:
702+
let safeLen = safeArrLen(collection) - 1
703+
rangeCheck(leftInd, rightInd, safeLen)
704+
regs[ra].node = newTree(
705+
nkTupleConstr,
706+
collection,
707+
newIntNode(nkIntLit, BiggestInt leftInd),
708+
newIntNode(nkIntLit, BiggestInt rightInd)
709+
)
710+
711+
661712
of opcLdArr:
662713
# a = b[c]
663714
decodeBC(rkNode)
664715
if regs[rc].intVal > high(int):
665716
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
666717
let idx = regs[rc].intVal.int
667718
let src = regs[rb].node
668-
if src.kind in {nkStrLit..nkTripleStrLit}:
719+
case src.kind
720+
of nkTupleConstr: # refer to `of opcSlice`
721+
let
722+
left = src[1].intVal
723+
right = src[2].intVal
724+
realIndex = left + idx
725+
if idx in 0..(right - left):
726+
case src[0].kind
727+
of nkStrKinds:
728+
regs[ra].node = newIntNode(nkCharLit, ord src[0].strVal[int realIndex])
729+
of nkBracket:
730+
regs[ra].node = src[0][int realIndex]
731+
else:
732+
stackTrace(c, tos, pc, "opcLdArr internal error")
733+
else:
734+
stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
735+
736+
of nkStrLit..nkTripleStrLit:
669737
if idx <% src.strVal.len:
670738
regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
671739
regs[ra].node.intVal = src.strVal[idx].ord
@@ -682,10 +750,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
682750
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
683751
let idx = regs[rc].intVal.int
684752
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
685-
if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
686-
takeAddress regs[ra], src.sons[idx]
753+
case src.kind
754+
of nkTupleConstr:
755+
let
756+
left = src[1].intVal
757+
right = src[2].intVal
758+
realIndex = left + idx
759+
if idx in 0..(right - left): # Refer to `opcSlice`
760+
case src[0].kind
761+
of nkStrKinds:
762+
regs[ra] = takeCharAddress(c, src[0], realIndex, pc)
763+
of nkBracket:
764+
takeAddress regs[ra], src.sons[0].sons[realIndex]
765+
else:
766+
stackTrace(c, tos, pc, "opcLdArrAddr internal error")
767+
else:
768+
stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
687769
else:
688-
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
770+
if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
771+
takeAddress regs[ra], src.sons[idx]
772+
else:
773+
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
689774
of opcLdStrIdx:
690775
decodeBC(rkInt)
691776
let idx = regs[rc].intVal.int
@@ -702,21 +787,32 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
702787
let idx = regs[rc].intVal.int
703788
let s = regs[rb].node.strVal.addr # or `byaddr`
704789
if idx <% s[].len:
705-
# `makePtrType` not accessible from vm.nim
706-
let typ = newType(tyPtr, nextTypeId c.idgen, c.module.owner)
707-
typ.add getSysType(c.graph, c.debug[pc], tyChar)
708-
let node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
709-
node.intVal = cast[int](s[][idx].addr)
710-
node.flags.incl nfIsPtr
711-
regs[ra].node = node
790+
regs[ra] = takeCharAddress(c, regs[rb].node, idx, pc)
712791
else:
713792
stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1))
714793
of opcWrArr:
715794
# a[b] = c
716795
decodeBC(rkNode)
717796
let idx = regs[rb].intVal.int
718797
let arr = regs[ra].node
719-
if arr.kind in {nkStrLit..nkTripleStrLit}:
798+
case arr.kind
799+
of nkTupleConstr: # refer to `opcSlice`
800+
let
801+
src = arr[0]
802+
left = arr[1].intVal
803+
right = arr[2].intVal
804+
realIndex = left + idx
805+
if idx in 0..(right - left):
806+
case src.kind
807+
of nkStrKinds:
808+
src.strVal[int(realIndex)] = char(regs[rc].intVal)
809+
of nkBracket:
810+
src[int(realIndex)] = regs[rc].node
811+
else:
812+
stackTrace(c, tos, pc, "opcWrArr internal error")
813+
else:
814+
stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
815+
of {nkStrLit..nkTripleStrLit}:
720816
if idx <% arr.strVal.len:
721817
arr.strVal[idx] = chr(regs[rc].intVal)
722818
else:
@@ -874,14 +970,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
874970
of opcLenSeq:
875971
decodeBImm(rkInt)
876972
#assert regs[rb].kind == nkBracket
877-
let high = (imm and 1) # discard flags
973+
let
974+
high = (imm and 1) # discard flags
975+
node = regs[rb].node
878976
if (imm and nimNodeFlag) != 0:
879977
# used by mNLen (NimNode.len)
880978
regs[ra].intVal = regs[rb].node.safeLen - high
881979
else:
882-
# safeArrLen also return string node len
883-
# used when string is passed as openArray in VM
884-
regs[ra].intVal = regs[rb].node.safeArrLen - high
980+
case node.kind
981+
of nkTupleConstr: # refer to `of opcSlice`
982+
regs[ra].intVal = node[2].intVal - node[1].intVal + 1 - high
983+
else:
984+
# safeArrLen also return string node len
985+
# used when string is passed as openArray in VM
986+
regs[ra].intVal = node.safeArrLen - high
987+
885988
of opcLenStr:
886989
decodeBImm(rkInt)
887990
assert regs[rb].kind == rkNode

compiler/vmdef.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type
8181
opcWrStrIdx,
8282
opcLdStrIdx, # a = b[c]
8383
opcLdStrIdxAddr, # a = addr(b[c])
84+
opcSlice, # toOpenArray(collection, left, right)
8485

8586
opcAddInt,
8687
opcAddImmInt,

compiler/vmgen.nim

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -643,9 +643,19 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
643643
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
644644
case le.kind
645645
of nkBracketExpr:
646-
let dest = c.genx(le[0], {gfNode})
647-
let idx = c.genIndex(le[1], le[0].typ)
648-
c.gABC(le, opcWrArr, dest, idx, value)
646+
let
647+
dest = c.genx(le[0], {gfNode})
648+
idx = c.genIndex(le[1], le[0].typ)
649+
collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
650+
651+
case collTyp.kind
652+
of tyString, tyCstring:
653+
c.gABC(le, opcWrStrIdx, dest, idx, value)
654+
of tyTuple:
655+
c.gABC(le, opcWrObj, dest, int le[1].intVal, value)
656+
else:
657+
c.gABC(le, opcWrArr, dest, idx, value)
658+
649659
c.freeTemp(dest)
650660
c.freeTemp(idx)
651661
of nkCheckedFieldExpr:
@@ -1057,6 +1067,18 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
10571067
of tyString: genUnaryABI(c, n, dest, opcLenStr)
10581068
of tyCstring: genUnaryABI(c, n, dest, opcLenCstring)
10591069
else: doAssert false, $n[1].typ.kind
1070+
of mSlice:
1071+
var
1072+
d = c.genx(n[1])
1073+
left = c.genIndex(n[2], n[1].typ)
1074+
right = c.genIndex(n[3], n[1].typ)
1075+
if dest < 0: dest = c.getTemp(n.typ)
1076+
c.gABC(n, opcNodeToReg, dest, d)
1077+
c.gABC(n, opcSlice, dest, left, right)
1078+
c.freeTemp(left)
1079+
c.freeTemp(right)
1080+
c.freeTemp(d)
1081+
10601082
of mIncl, mExcl:
10611083
unused(c, n, dest)
10621084
var d = c.genx(n[1])
@@ -1182,7 +1204,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
11821204
var d = c.genx(n[1])
11831205
# XXX use ldNullOpcode() here?
11841206
c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
1185-
c.gABx(n, opcNodeToReg, d, d)
1207+
c.gABC(n, opcNodeToReg, d, d)
11861208
c.genAsgnPatch(n[1], d)
11871209
of mDefault:
11881210
if dest < 0: dest = c.getTemp(n.typ)
@@ -1526,12 +1548,16 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
15261548
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
15271549
case le.kind
15281550
of nkBracketExpr:
1529-
let dest = c.genx(le[0], {gfNode})
1530-
let idx = c.genIndex(le[1], le[0].typ)
1531-
let tmp = c.genx(ri)
1532-
if le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
1533-
tyString, tyCstring}:
1551+
let
1552+
dest = c.genx(le[0], {gfNode})
1553+
idx = c.genIndex(le[1], le[0].typ)
1554+
tmp = c.genx(ri)
1555+
collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
1556+
case collTyp.kind
1557+
of tyString, tyCstring:
15341558
c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp)
1559+
of tyTuple:
1560+
c.preventFalseAlias(le, opcWrObj, dest, int le[1].intVal, tmp)
15351561
else:
15361562
c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
15371563
c.freeTemp(tmp)
@@ -1695,9 +1721,7 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
16951721
c.freeTemp(a)
16961722
c.freeTemp(b)
16971723

1698-
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
1699-
let a = c.genx(n[0], flags)
1700-
let b = genField(c, n[1])
1724+
proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenFlags) =
17011725
if dest < 0: dest = c.getTemp(n.typ)
17021726
if {gfNodeAddr} * flags != {}:
17031727
c.gABC(n, opcLdObjAddr, dest, a, b)
@@ -1710,6 +1734,11 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
17101734
c.gABC(n, opcLdObj, dest, a, b)
17111735
c.freeTemp(a)
17121736

1737+
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
1738+
genObjAccessAux(c, n, c.genx(n[0], flags), genField(c, n[1]), dest, flags)
1739+
1740+
1741+
17131742
proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
17141743
internalAssert c.config, n.kind == nkCheckedFieldExpr
17151744
# nkDotExpr to access the requested field
@@ -1777,10 +1806,13 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
17771806

17781807
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
17791808
let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
1780-
if arrayType in {tyString, tyCstring}:
1809+
case arrayType
1810+
of tyString, tyCstring:
17811811
let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx
17821812
genArrAccessOpcode(c, n, dest, opc, flags)
1783-
elif arrayType == tyTypeDesc:
1813+
of tyTuple:
1814+
c.genObjAccessAux(n, c.genx(n[0], flags), int n[1].intVal, dest, flags)
1815+
of tyTypeDesc:
17841816
c.genTypeLit(n.typ, dest)
17851817
else:
17861818
let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr

tests/vm/topenarrays.nim

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
proc mutate(a: var openarray[int]) =
2+
var i = 0
3+
for x in a.mitems:
4+
x = i
5+
inc i
6+
7+
proc mutate(a: var openarray[char]) =
8+
var i = 1
9+
for ch in a.mitems:
10+
ch = 'a'
11+
12+
13+
static:
14+
var a = [10, 20, 30]
15+
assert a.toOpenArray(1, 2).len == 2
16+
17+
mutate(a)
18+
assert a.toOpenArray(0, 2) == [0, 1, 2]
19+
assert a.toOpenArray(0, 0) == [0]
20+
assert a.toOpenArray(1, 2) == [1, 2]
21+
assert "Hello".toOpenArray(1, 4) == "ello"
22+
var str = "Hello"
23+
str.toOpenArray(2, 4).mutate()
24+
assert str.toOpenArray(0, 4).len == 5
25+
assert str.toOpenArray(0, 0).len == 1
26+
assert str.toOpenArray(0, 0).high == 0
27+
assert str == "Heaaa"
28+
assert str.toOpenArray(0, 4) == "Heaaa"
29+
30+
var arr: array[3..4, int] = [1, 2]
31+
assert arr.toOpenArray(3, 4) == [1, 2]
32+
assert arr.toOpenArray(3, 4).len == 2
33+
assert arr.toOpenArray(3, 3).high == 0
34+
35+
assert arr.toOpenArray(3, 4).toOpenArray(0, 0) == [1]
36+
37+
38+

0 commit comments

Comments
 (0)