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

feat[venom]: add calloca instruction #4376

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
23 changes: 22 additions & 1 deletion vyper/codegen/self_call.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import copy

from vyper.codegen.core import _freshname, eval_once_check, make_setter
from vyper.codegen.ir_node import IRnode
from vyper.evm.address_space import MEMORY
Expand Down Expand Up @@ -66,7 +68,26 @@ def ir_for_self_call(stmt_expr, context):

# note: dst_tuple_t != args_tuple_t
dst_tuple_t = TupleT(tuple(func_t.argument_types))
args_dst = IRnode(func_t._ir_info.frame_info.frame_start, typ=dst_tuple_t, location=MEMORY)
if context.settings.experimental_codegen:
arg_items = ["multi"]
frame_info = func_t._ir_info.frame_info

for var in frame_info.frame_vars.values():
var = copy.copy(var)
alloca = var.alloca
assert alloca is not None
assert isinstance(var.pos, str) # help mypy
if not var.pos.startswith("$palloca"):
continue
newname = var.pos.replace("$palloca", "$calloca")
var.pos = newname
irnode = var.as_ir_node()
irnode.passthrough_metadata["alloca"] = alloca
arg_items.append(irnode)
args_dst = IRnode.from_list(arg_items, typ=dst_tuple_t)
else:
# legacy
args_dst = IRnode(func_t._ir_info.frame_info.frame_start, typ=dst_tuple_t, location=MEMORY)

# if one of the arguments is a self call, the argument
# buffer could get borked. to prevent against that,
Expand Down
5 changes: 5 additions & 0 deletions vyper/venom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ Assembly can be inspected with `-f asm`, whereas an opcode view of the final byt
out = palloca size, offset, id
```
- Like the `alloca` instruction but only used for parameters of internal functions which are passed by memory.
- `calloca`
- ```
out = calloca size, offset, id
```
- Similar to the `calloca` instruction but only used for parameters of internal functions which are passed by memory. Used at the call-site of a call.
- `iload`
- ```
out = iload offset
Expand Down
15 changes: 15 additions & 0 deletions vyper/venom/ir_node_to_venom.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,14 @@ def _handle_self_call(fn: IRFunction, ir: IRnode, symbols: SymbolTable) -> Optio
def _handle_internal_func(
fn: IRFunction, ir: IRnode, does_return_data: bool, symbols: SymbolTable
) -> IRFunction:
global _alloca_table

fn = fn.ctx.create_function(ir.args[0].args[0].value)
bb = fn.get_basic_block()

_saved_alloca_table = _alloca_table
_alloca_table = {}

# return buffer
if does_return_data:
symbols["return_buffer"] = bb.append_instruction("param")
Expand All @@ -191,6 +196,7 @@ def _handle_internal_func(

_convert_ir_bb(fn, ir.args[0].args[2], symbols)

_alloca_table = _saved_alloca_table
return fn


Expand Down Expand Up @@ -542,6 +548,15 @@ def emit_body_blocks():
_alloca_table[alloca._id] = ptr
return _alloca_table[alloca._id]

elif ir.value.startswith("$calloca"):
alloca = ir.passthrough_metadata["alloca"]
if alloca._id not in _alloca_table:
ptr = fn.get_basic_block().append_instruction(
"calloca", alloca.offset, alloca.size, alloca._id
)
_alloca_table[alloca._id] = ptr
return _alloca_table[alloca._id]

return symbols.get(ir.value)
elif ir.is_literal:
return IRLiteral(ir.value)
Expand Down
2 changes: 1 addition & 1 deletion vyper/venom/passes/float_allocas.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def run_pass(self):
# Extract alloca instructions
non_alloca_instructions = []
for inst in bb.instructions:
if inst.opcode in ("alloca", "palloca"):
if inst.opcode in ("alloca", "palloca", "calloca"):
# note: order of allocas impacts bytecode.
# TODO: investigate.
entry_bb.insert_instruction(inst)
Expand Down
2 changes: 1 addition & 1 deletion vyper/venom/passes/sccp/sccp.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def _visit_phi(self, inst: IRInstruction):

def _visit_expr(self, inst: IRInstruction):
opcode = inst.opcode
if opcode in ["store", "alloca", "palloca"]:
if opcode in ["store", "alloca", "palloca", "calloca"]:
assert inst.output is not None, "Got store/alloca without output"
out = self._eval_from_lattice(inst.operands[0])
self._set_lattice(inst.output, out)
Expand Down
4 changes: 2 additions & 2 deletions vyper/venom/venom_to_assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ def _generate_evm_for_instruction(

if opcode in ["jmp", "djmp", "jnz", "invoke"]:
operands = list(inst.get_non_label_operands())
elif opcode in ("alloca", "palloca"):
elif opcode in ("alloca", "palloca", "calloca"):
offset, _size = inst.operands
operands = [offset]

Expand Down Expand Up @@ -464,7 +464,7 @@ def _generate_evm_for_instruction(
# Step 5: Emit the EVM instruction(s)
if opcode in _ONE_TO_ONE_INSTRUCTIONS:
assembly.append(opcode.upper())
elif opcode in ("alloca", "palloca"):
elif opcode in ("alloca", "palloca", "calloca"):
pass
elif opcode == "param":
pass
Expand Down
Loading