Skip to content
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
13 changes: 10 additions & 3 deletions pyteal/compiler/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,14 @@ def createConstantBlocks(ops: List[TealComponent]) -> List[TealComponent]:
sortedInts = sorted(intFreqs, key=lambda x: intFreqs[x], reverse=True)
sortedBytes = sorted(byteFreqs, key=lambda x: byteFreqs[x], reverse=True)

intBlock = [i for i in sortedInts if intFreqs[i] > 1]
# Use Op.pushint if the constant does not occur in the top 4 most frequent and is smaller than
# 2 ** 7 to improve performance and save block space.
intBlock = [
val
for i, val in enumerate(sortedInts)
if intFreqs[val] > 1 and (i < 4 or isinstance(val, str) or val >= 2 ** 7)
]

byteBlock = [
("0x" + b.hex()) if type(b) is bytes else cast(str, b)
for b in sortedBytes
Expand All @@ -151,13 +158,13 @@ def createConstantBlocks(ops: List[TealComponent]) -> List[TealComponent]:

if basicOp == Op.int:
intValue = extractIntValue(op)
if intFreqs[intValue] == 1:
if intValue not in intBlock:
assembled.append(
TealOp(op.expr, Op.pushint, intValue, "//", *op.args)
)
continue

index = sortedInts.index(intValue)
index = intBlock.index(intValue)
if index == 0:
assembled.append(TealOp(op.expr, Op.intc_0, "//", *op.args))
elif index == 1:
Expand Down
74 changes: 74 additions & 0 deletions pyteal/compiler/constants_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,3 +545,77 @@ def test_createConstantBlocks_tmpl_all():

actual = createConstantBlocks(ops)
assert actual == expected


def test_createConstantBlocks_intc():
"""Test scenario where there are more than 4 constants in the intcblock.
If the 4th constant can't fit in one varuint byte (more than 2**7) it
should be referenced with the Op.intc 4 command.
"""

ops = [
TealOp(None, Op.int, 0),
TealOp(None, Op.int, 0),
TealOp(None, Op.int, 1),
TealOp(None, Op.int, 1),
TealOp(None, Op.int, 2),
TealOp(None, Op.int, 2),
TealOp(None, Op.int, 3),
TealOp(None, Op.int, 3),
TealOp(None, Op.int, 2 ** 7),
TealOp(None, Op.int, 2 ** 7),
]

expected = [
TealOp(None, Op.intcblock, 0, 1, 2, 3, 2 ** 7),
TealOp(None, Op.intc_0, "//", 0),
TealOp(None, Op.intc_0, "//", 0),
TealOp(None, Op.intc_1, "//", 1),
TealOp(None, Op.intc_1, "//", 1),
TealOp(None, Op.intc_2, "//", 2),
TealOp(None, Op.intc_2, "//", 2),
TealOp(None, Op.intc_3, "//", 3),
TealOp(None, Op.intc_3, "//", 3),
TealOp(None, Op.intc, 4, "//", 2 ** 7),
TealOp(None, Op.intc, 4, "//", 2 ** 7),
]

actual = createConstantBlocks(ops)
assert actual == expected


def test_createConstantBlocks_small_constant():
"""If a constant cannot be referenced using the intc_[0..3] commands
and it can be stored in one varuint it byte then Op.pushint is used.
"""

for cur in range(4, 2 ** 7):
ops = [
TealOp(None, Op.int, 0),
TealOp(None, Op.int, 0),
TealOp(None, Op.int, 1),
TealOp(None, Op.int, 1),
TealOp(None, Op.int, 2),
TealOp(None, Op.int, 2),
TealOp(None, Op.int, 3),
TealOp(None, Op.int, 3),
TealOp(None, Op.int, cur),
TealOp(None, Op.int, cur),
]

expected = [
TealOp(None, Op.intcblock, 0, 1, 2, 3),
TealOp(None, Op.intc_0, "//", 0),
TealOp(None, Op.intc_0, "//", 0),
TealOp(None, Op.intc_1, "//", 1),
TealOp(None, Op.intc_1, "//", 1),
TealOp(None, Op.intc_2, "//", 2),
TealOp(None, Op.intc_2, "//", 2),
TealOp(None, Op.intc_3, "//", 3),
TealOp(None, Op.intc_3, "//", 3),
TealOp(None, Op.pushint, cur, "//", cur),
TealOp(None, Op.pushint, cur, "//", cur),
]

actual = createConstantBlocks(ops)
assert actual == expected