Skip to content

Commit ec51769

Browse files
vaivaswathaironcev
andauthored
Introduce alloc intrinsic to avoid using asm (#7499)
Improvement chart: #7499 (comment) --------- Co-authored-by: Igor Rončević <[email protected]>
1 parent 936e752 commit ec51769

File tree

22 files changed

+1493
-1256
lines changed

22 files changed

+1493
-1256
lines changed

docs/book/src/reference/compiler_intrinsics.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,3 +435,13 @@ This number is not guaranteed to be stable on different compiler versions.
435435
`__encoding_mem_id` represents how the type is encoded. It returns 0 when a type does not have encoding representation.
436436

437437
**Constraints:** None
438+
439+
---
440+
441+
```sway
442+
__alloc<T>(count: u64) -> raw_ptr
443+
```
444+
445+
**Description:** Allocate `count` contiguous elements of `T` on the heap and return a pointer to the newly allocated memory.
446+
447+
**Constraints** None

sway-ast/src/intrinsics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub enum Intrinsic {
4646
ElemAt, // let elem: &T = __elem_at::<T: array or ref_to_slice>(item: T, index)
4747
Transmute, // let dst: B = __transmute::<A, B>(src)
4848
Dbg, // __dbg(value)
49+
Alloc, // __alloc<T>(size: u64) -> raw_ptr
4950
RuntimeMemoryId, // __runtime_mem_id::<T>() -> u64
5051
EncodingMemoryId, // __encoding_mem_id::<T>() -> u64
5152
}
@@ -96,6 +97,7 @@ impl fmt::Display for Intrinsic {
9697
Intrinsic::ElemAt => "elem_at",
9798
Intrinsic::Transmute => "transmute",
9899
Intrinsic::Dbg => "dbg",
100+
Intrinsic::Alloc => "alloc",
99101
Intrinsic::RuntimeMemoryId => "runtime_mem_id",
100102
Intrinsic::EncodingMemoryId => "encoding_mem_id",
101103
};
@@ -150,6 +152,7 @@ impl Intrinsic {
150152
"__elem_at" => ElemAt,
151153
"__transmute" => Transmute,
152154
"__dbg" => Dbg,
155+
"__alloc" => Alloc,
153156
"__runtime_mem_id" => RuntimeMemoryId,
154157
"__encoding_mem_id" => EncodingMemoryId,
155158
_ => return None,

sway-core/src/asm_generation/evm/evm_asm_builder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ impl<'ir, 'eng> EvmAsmBuilder<'ir, 'eng> {
347347
}
348348
InstOp::IntToPtr(val, _) => self.compile_int_to_ptr(instr_val, val),
349349
InstOp::Load(src_val) => self.compile_load(handler, instr_val, src_val)?,
350+
InstOp::Alloc { ty, count } => self.compile_alloc(instr_val, ty, count),
350351
InstOp::MemCopyBytes {
351352
dst_val_ptr,
352353
src_val_ptr,
@@ -501,6 +502,10 @@ impl<'ir, 'eng> EvmAsmBuilder<'ir, 'eng> {
501502
todo!();
502503
}
503504

505+
fn compile_alloc(&mut self, instr_val: &Value, ty: &Type, count: &Value) {
506+
todo!();
507+
}
508+
504509
fn compile_log(&mut self, instr_val: &Value, log_val: &Value, log_ty: &Type, log_id: &Value) {
505510
todo!();
506511
}

sway-core/src/asm_generation/fuel/fuel_asm_builder.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
485485
}
486486
InstOp::IntToPtr(val, _) => self.compile_no_op_move(instr_val, val),
487487
InstOp::Load(src_val) => self.compile_load(instr_val, src_val),
488+
InstOp::Alloc { ty, count } => self.compile_alloc(instr_val, ty, count),
488489
InstOp::MemCopyBytes {
489490
dst_val_ptr,
490491
src_val_ptr,
@@ -1467,6 +1468,78 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
14671468
Ok(())
14681469
}
14691470

1471+
fn compile_alloc(
1472+
&mut self,
1473+
instr_val: &Value,
1474+
ty: &Type,
1475+
count: &Value,
1476+
) -> Result<(), CompileError> {
1477+
let owning_span = self.md_mgr.val_to_span(self.context, *instr_val);
1478+
1479+
let ty_size = ty.size(self.context).in_bytes();
1480+
1481+
let statically_known_count = count.get_constant(self.context).and_then(|count| {
1482+
if let ConstantValue::Uint(count_uint) = count.get_content(self.context).value {
1483+
Some(count_uint)
1484+
} else {
1485+
None
1486+
}
1487+
});
1488+
1489+
let size_reg = self.reg_seqr.next();
1490+
// If we know the count statically, multiply it by the type size to get the total size.
1491+
// Otherwise, just load the type size and multiply later.
1492+
if let Some(statically_known_count) = statically_known_count {
1493+
self.immediate_to_reg(
1494+
ty_size * statically_known_count,
1495+
size_reg.clone(),
1496+
None,
1497+
"get total allocation size in bytes",
1498+
owning_span.clone(),
1499+
);
1500+
} else {
1501+
self.immediate_to_reg(
1502+
ty_size,
1503+
size_reg.clone(),
1504+
None,
1505+
"get size of allocation element type",
1506+
owning_span.clone(),
1507+
);
1508+
let count_reg = self.value_to_register(count)?;
1509+
self.cur_bytecode.push(Op {
1510+
opcode: Either::Left(VirtualOp::MUL(
1511+
size_reg.clone(),
1512+
size_reg.clone(),
1513+
count_reg,
1514+
)),
1515+
comment: "get total allocation size in bytes".into(),
1516+
owning_span: owning_span.clone(),
1517+
});
1518+
}
1519+
1520+
// Actually perform the allocation.
1521+
self.cur_bytecode.push(Op {
1522+
opcode: Either::Left(VirtualOp::ALOC(
1523+
VirtualRegister::Constant(ConstantRegister::HeapPointer),
1524+
size_reg,
1525+
)),
1526+
comment: "allocate memory".into(),
1527+
owning_span,
1528+
});
1529+
1530+
let instr_reg = self.reg_seqr.next();
1531+
// Move the resulting pointer from the heap pointer register to the instruction register.
1532+
self.cur_bytecode.push(Op::register_move(
1533+
instr_reg.clone(),
1534+
VirtualRegister::Constant(ConstantRegister::HeapPointer),
1535+
"save allocated memory pointer",
1536+
self.md_mgr.val_to_span(self.context, *instr_val),
1537+
));
1538+
1539+
self.reg_map.insert(*instr_val, instr_reg);
1540+
Ok(())
1541+
}
1542+
14701543
fn compile_load(&mut self, instr_val: &Value, src_val: &Value) -> Result<(), CompileError> {
14711544
let owning_span = self.md_mgr.val_to_span(self.context, *instr_val);
14721545
let src_ty = src_val

sway-core/src/ir_generation/const_eval.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@ fn const_eval_intrinsic(
13181318
}
13191319
},
13201320
Intrinsic::AddrOf
1321+
| Intrinsic::Alloc
13211322
| Intrinsic::PtrAdd
13221323
| Intrinsic::PtrSub
13231324
| Intrinsic::IsReferenceType

sway-core/src/ir_generation/function.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,33 @@ impl<'a> FnCompiler<'a> {
14911491
}
14921492
}
14931493
}
1494+
Intrinsic::Alloc => {
1495+
let targ = type_arguments[0].clone();
1496+
let ir_type = convert_resolved_type_id(
1497+
self.engines,
1498+
context,
1499+
md_mgr,
1500+
self.module,
1501+
Some(self),
1502+
targ.type_id(),
1503+
&targ.span(),
1504+
)?;
1505+
let count_exp = &arguments[0];
1506+
let count_value = return_on_termination_or_extract!(
1507+
self.compile_expression_to_register(context, md_mgr, count_exp)?
1508+
)
1509+
.expect_register();
1510+
let span_md_idx = md_mgr.span_to_md(context, &span);
1511+
let val = self
1512+
.current_block
1513+
.append(context)
1514+
.alloc(ir_type, count_value)
1515+
.add_metadatum(context, span_md_idx);
1516+
Ok(TerminatorValue::new(
1517+
CompiledValue::InRegister(val),
1518+
context,
1519+
))
1520+
}
14941521
Intrinsic::Add
14951522
| Intrinsic::Sub
14961523
| Intrinsic::Mul

sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ impl ty::TyIntrinsicFunctionKind {
119119
Intrinsic::EncodingMemoryId => {
120120
type_check_encoding_memory_id(arguments, handler, kind, type_arguments, span, ctx)
121121
}
122+
Intrinsic::Alloc => {
123+
type_check_alloc(handler, ctx, kind, arguments, type_arguments, span)
124+
}
122125
}
123126
}
124127
}
@@ -215,6 +218,65 @@ fn type_check_runtime_memory_id(
215218
Ok((intrinsic_function, ctx.engines.te().id_of_u64()))
216219
}
217220

221+
fn type_check_alloc(
222+
handler: &Handler,
223+
mut ctx: TypeCheckContext,
224+
kind: Intrinsic,
225+
arguments: &[Expression],
226+
type_arguments: &[GenericArgument],
227+
span: Span,
228+
) -> Result<(TyIntrinsicFunctionKind, TypeId), ErrorEmitted> {
229+
if arguments.len() != 1 {
230+
return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs {
231+
name: kind.to_string(),
232+
expected: 1,
233+
span,
234+
}));
235+
}
236+
237+
let engines = ctx.engines();
238+
239+
// Type argument needs to be explicitly defined
240+
if type_arguments.len() != 1 {
241+
return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumTArgs {
242+
name: kind.to_string(),
243+
expected: 1,
244+
span,
245+
}));
246+
}
247+
248+
let alloc_type = ctx
249+
.resolve_type(
250+
handler,
251+
type_arguments[0].type_id(),
252+
&type_arguments[0].span(),
253+
EnforceTypeArguments::Yes,
254+
None,
255+
)
256+
.unwrap_or_else(|err| engines.te().id_of_error_recovery(err));
257+
258+
// type check first argument, ensure that it is u64
259+
let first_argument_typed_expr = {
260+
let ctx = ctx
261+
.by_ref()
262+
.with_help_text("")
263+
.with_type_annotation(engines.te().id_of_u64());
264+
ty::TyExpression::type_check(handler, ctx, &arguments[0])?
265+
};
266+
267+
let mut final_type_arguments = type_arguments.to_vec();
268+
*final_type_arguments[0].type_id_mut() = alloc_type;
269+
Ok((
270+
TyIntrinsicFunctionKind {
271+
kind,
272+
arguments: vec![first_argument_typed_expr],
273+
type_arguments: final_type_arguments,
274+
span,
275+
},
276+
engines.te().id_of_raw_ptr(),
277+
))
278+
}
279+
218280
fn type_check_transmute(
219281
arguments: &[Expression],
220282
handler: &Handler,

sway-core/src/semantic_analysis/cei_pattern_analysis.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet<Effect> {
677677
| ElemAt
678678
| Transmute
679679
| Dbg
680+
| Alloc
680681
| RuntimeMemoryId
681682
| EncodingMemoryId => HashSet::new(),
682683
}

sway-ir/src/analysis/memory_utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ fn compute_escaped_symbols(context: &Context, function: &Function) -> EscapedSym
453453
InstOp::Store { stored_val, .. } => {
454454
add_from_val(&mut result, stored_val, &mut is_complete)
455455
}
456+
InstOp::Alloc { .. } => (),
456457
}
457458
}
458459

@@ -519,6 +520,7 @@ pub fn get_loaded_ptr_values(context: &Context, inst: Value) -> Vec<Value> {
519520
..
520521
}) => vec![*memopd1, *memopd2],
521522
InstOp::Store { dst_val_ptr: _, .. } => vec![],
523+
InstOp::Alloc { .. } => vec![],
522524
InstOp::FuelVm(FuelVmInstruction::Gtf { .. })
523525
| InstOp::FuelVm(FuelVmInstruction::ReadRegister(_))
524526
| InstOp::FuelVm(FuelVmInstruction::Revert(_) | FuelVmInstruction::JmpMem) => vec![],
@@ -578,6 +580,7 @@ pub fn get_stored_ptr_values(context: &Context, inst: Value) -> Vec<Value> {
578580
| InstOp::MemClearVal { dst_val_ptr }
579581
| InstOp::Store { dst_val_ptr, .. } => vec![*dst_val_ptr],
580582
InstOp::Load(_) => vec![],
583+
InstOp::Alloc { .. } => vec![],
581584
InstOp::FuelVm(vmop) => match vmop {
582585
FuelVmInstruction::Gtf { .. }
583586
| FuelVmInstruction::Log { .. }

sway-ir/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub enum IrError {
4646
VerifyIntToPtrFromNonIntegerType(String),
4747
VerifyIntToPtrToNonPointer(String),
4848
VerifyIntToPtrUnknownSourceType,
49+
VerifyAllocCountNotUint64,
4950
VerifyInvalidGtfIndexType,
5051
VerifyLoadFromNonPointer(String),
5152
VerifyLocalMissingInitializer(String, String),
@@ -280,6 +281,12 @@ impl fmt::Display for IrError {
280281
f,
281282
"Verification failed: int_to_ptr unable to determine source type."
282283
),
284+
IrError::VerifyAllocCountNotUint64 => {
285+
write!(
286+
f,
287+
"Verification failed: alloc instruction count must be a u64 integer."
288+
)
289+
}
283290
IrError::VerifyLoadFromNonPointer(ty) => {
284291
write!(
285292
f,

0 commit comments

Comments
 (0)