Skip to content

Commit

Permalink
Implement pop for memory arrays
Browse files Browse the repository at this point in the history
Signed-off-by: Keith Yeung <kungfukeith11@gmail.com>
  • Loading branch information
KiChjang committed Aug 1, 2020
1 parent e8adeba commit 6836e31
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 13 deletions.
11 changes: 11 additions & 0 deletions src/codegen/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ pub enum Instr {
array: usize,
value: Box<Expression>,
},
PopMemory {
res: usize,
ty: Type,
array: usize,
},
Set {
res: usize,
expr: Expression,
Expand Down Expand Up @@ -622,6 +627,12 @@ impl ControlFlowGraph {
ty.to_string(ns),
self.expr_to_string(contract, ns, value),
),
Instr::PopMemory { res, ty, array } => format!(
"%{}, %{} = pop array ty:{}",
self.vars[*res].id.name,
self.vars[*array].id.name,
ty.to_string(ns),
),
Instr::AssertFailure { expr: None } => "assert-failure".to_string(),
Instr::AssertFailure { expr: Some(expr) } => {
format!("assert-failure:{}", self.expr_to_string(contract, ns, expr))
Expand Down
27 changes: 27 additions & 0 deletions src/codegen/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,33 @@ pub fn expression(

Expression::Variable(*loc, elem_ty, address_res)
}
Expression::DynamicArrayPop(loc, array, ty) => {
let elem_ty = match ty {
Type::Array(..) => match ty.array_elem() {
elem @ Type::Struct(..) => Type::Ref(Box::new(elem)),
elem => elem,
},
Type::DynamicBytes => Type::Uint(8),
_ => unreachable!(),
};
let address_res = vartab.temp_anonymous(&elem_ty);

let address_arr = match expression(array, cfg, contract_no, ns, vartab) {
Expression::Variable(_, _, pos) => pos,
_ => unreachable!(),
};

cfg.add(
vartab,
Instr::PopMemory {
res: address_res,
ty: ty.clone(),
array: address_arr,
},
);

Expression::Variable(*loc, elem_ty, address_res)
}
Expression::StorageBytesLength(loc, expr) => Expression::StorageBytesLength(
*loc,
Box::new(expression(expr, cfg, contract_no, ns, vartab)),
Expand Down
149 changes: 149 additions & 0 deletions src/emit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,155 @@ impl<'a> Contract<'a> {
);
self.builder.build_store(size_field, new_len);
}
cfg::Instr::PopMemory { res, ty, array } => {
let a = w.vars[*array].value.into_pointer_value();
let len = unsafe {
self.builder.build_gep(
a,
&[
self.context.i32_type().const_zero(),
self.context.i32_type().const_zero(),
],
"a_len",
)
};
let len = self.builder.build_load(len, "a_len").into_int_value();

// First check if the array is empty
let is_array_empty = self.builder.build_int_compare(
IntPredicate::EQ,
len,
self.context.i32_type().const_zero(),
"is_array_empty",
);
let error = self.context.append_basic_block(function, "error");
let pop = self.context.append_basic_block(function, "pop");
self.builder
.build_conditional_branch(is_array_empty, error, pop);

self.builder.position_at_end(error);
runtime.assert_failure(
self,
self.context
.i8_type()
.ptr_type(AddressSpace::Generic)
.const_null(),
self.context.i32_type().const_zero(),
);

self.builder.position_at_end(pop);
let llvm_ty = self.llvm_type(ty);

// Calculate total size for reallocation
let elem_ty = match ty {
ast::Type::Array(..) => match self.llvm_type(&ty.array_elem()) {
elem @ BasicTypeEnum::StructType(_) => {
// We don't store structs directly in the array, instead we store references to structs
elem.ptr_type(AddressSpace::Generic).as_basic_type_enum()
}
elem => elem,
},
ast::Type::DynamicBytes => self.context.i8_type().into(),
_ => unreachable!(),
};
let elem_size = elem_ty
.size_of()
.unwrap()
.const_cast(self.context.i32_type(), false);
let new_len = self.builder.build_int_sub(
len,
self.context.i32_type().const_int(1, false),
"",
);
let vec_size = self
.module
.get_type("struct.vector")
.unwrap()
.size_of()
.unwrap()
.const_cast(self.context.i32_type(), false);
let size = self.builder.build_int_mul(elem_size, new_len, "");
let size = self.builder.build_int_add(size, vec_size, "");

// Get the pointer to the last element and return it
let slot_ptr = unsafe {
self.builder.build_gep(
a,
&[
self.context.i32_type().const_zero(),
self.context.i32_type().const_int(2, false),
self.builder.build_int_mul(new_len, elem_size, ""),
],
"data",
)
};
let slot_ptr = self.builder.build_pointer_cast(
slot_ptr,
elem_ty.ptr_type(AddressSpace::Generic),
"slot_ptr",
);
let ret_val = self.builder.build_load(slot_ptr, "");
w.vars[*res].value = ret_val;

// Reallocate and reassign the array pointer
let a = self.builder.build_pointer_cast(
a,
self.context.i8_type().ptr_type(AddressSpace::Generic),
"a",
);
let new = self
.builder
.build_call(
self.module.get_function("__realloc").unwrap(),
&[a.into(), size.into()],
"",
)
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let dest = self.builder.build_pointer_cast(
new,
llvm_ty.ptr_type(AddressSpace::Generic),
"dest",
);
w.vars[*array].value = dest.into();

// Update the len and size field of the vector struct
let len_ptr = unsafe {
self.builder.build_gep(
dest,
&[
self.context.i32_type().const_zero(),
self.context.i32_type().const_zero(),
],
"len",
)
};
let len_field = self.builder.build_pointer_cast(
len_ptr,
self.context.i32_type().ptr_type(AddressSpace::Generic),
"len field",
);
self.builder.build_store(len_field, new_len);

let size_ptr = unsafe {
self.builder.build_gep(
dest,
&[
self.context.i32_type().const_zero(),
self.context.i32_type().const_int(1, false),
],
"size",
)
};
let size_field = self.builder.build_pointer_cast(
size_ptr,
self.context.i32_type().ptr_type(AddressSpace::Generic),
"size field",
);
self.builder.build_store(size_field, new_len);
}
cfg::Instr::AssertFailure { expr: None } => {
runtime.assert_failure(
self,
Expand Down
7 changes: 4 additions & 3 deletions src/sema/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ pub enum Expression {
DynamicArrayLength(pt::Loc, Box<Expression>),
DynamicArraySubscript(pt::Loc, Type, Box<Expression>, Box<Expression>),
DynamicArrayPush(pt::Loc, Box<Expression>, Type, Box<Expression>),
DynamicArrayPop(pt::Loc, Box<Expression>, Type),
StorageBytesSubscript(pt::Loc, Box<Expression>, Box<Expression>),
StorageBytesPush(pt::Loc, Box<Expression>, Box<Expression>),
StorageBytesPop(pt::Loc, Box<Expression>),
Expand Down Expand Up @@ -879,9 +880,9 @@ impl Expression {
left.recurse(cx, f);
right.recurse(cx, f);
}
Expression::StorageBytesPop(_, expr) | Expression::StorageBytesLength(_, expr) => {
expr.recurse(cx, f)
}
Expression::StorageBytesPop(_, expr)
| Expression::StorageBytesLength(_, expr)
| Expression::DynamicArrayPop(_, expr, _) => expr.recurse(cx, f),
Expression::StringCompare(_, left, right)
| Expression::StringConcat(_, _, left, right) => {
if let StringLocation::RunTime(expr) = left {
Expand Down
40 changes: 30 additions & 10 deletions src/sema/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ impl Expression {
| Expression::DynamicArrayLength(loc, _)
| Expression::DynamicArraySubscript(loc, _, _, _)
| Expression::DynamicArrayPush(loc, _, _, _)
| Expression::DynamicArrayPop(loc, _, _)
| Expression::StorageBytesSubscript(loc, _, _)
| Expression::StorageBytesPush(loc, _, _)
| Expression::StorageBytesPop(loc, _)
Expand Down Expand Up @@ -169,11 +170,13 @@ impl Expression {
| Expression::GetAddress(_, ty)
| Expression::Keccak256(_, ty, _)
| Expression::Assign(_, ty, _, _) => ty.clone(),
Expression::DynamicArrayPush(_, _, ty, _) => match ty {
Type::Array(..) => ty.array_elem(),
Type::DynamicBytes => Type::Uint(8),
_ => unreachable!(),
},
Expression::DynamicArrayPush(_, _, ty, _) | Expression::DynamicArrayPop(_, _, ty) => {
match ty {
Type::Array(..) => ty.array_elem(),
Type::DynamicBytes => Type::Uint(8),
_ => unreachable!(),
}
}
Expression::DynamicArrayLength(_, _) => Type::Uint(32),
Expression::StorageBytesLength(_, _) => Type::Uint(32),
Expression::StorageBytesSubscript(_, _, _) => {
Expand Down Expand Up @@ -219,11 +222,13 @@ impl Expression {
| Expression::ExternalFunctionCall { returns, .. } => returns.to_vec(),
Expression::List(_, list) => list.iter().map(|e| e.ty()).collect(),
Expression::ExternalFunctionCallRaw { .. } => vec![Type::Bool, Type::DynamicBytes],
Expression::DynamicArrayPush(_, _, ty, _) => match ty {
Type::Array(..) => vec![ty.array_elem()],
Type::DynamicBytes => vec![Type::Uint(8)],
_ => unreachable!(),
},
Expression::DynamicArrayPush(_, _, ty, _) | Expression::DynamicArrayPop(_, _, ty) => {
match ty {
Type::Array(..) => vec![ty.array_elem()],
Type::DynamicBytes => vec![Type::Uint(8)],
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -3739,6 +3744,21 @@ fn method_call_pos_args(
Box::new(val),
));
}
if func.name == "pop" {
if !args.is_empty() {
ns.diagnostics.push(Diagnostic::error(
func.loc,
"method ‘pop()’ does not take any arguments".to_string(),
));
return Err(());
}

return Ok(Expression::DynamicArrayPop(
*loc,
Box::new(var_expr),
var_ty,
));
}
}

if let Type::Contract(contract_no) = &var_ty.deref_any() {
Expand Down
Loading

0 comments on commit 6836e31

Please sign in to comment.