Skip to content

Commit 4f5a364

Browse files
committed
Initial support for create1/create2 and transient storage
1 parent 50aac64 commit 4f5a364

File tree

14 files changed

+646
-92
lines changed

14 files changed

+646
-92
lines changed

integration/stylus/milestone_2.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity >=0.8.0;
33

4+
contract T {
5+
function test() public view returns (uint256) {
6+
return 1;
7+
}
8+
}
9+
410
contract C {
11+
bytes32 private constant REENTRANCY_GUARD_STORAGE =
12+
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
13+
514
function test()
615
public
716
view
@@ -25,4 +34,14 @@ contract C {
2534
block_chainid
2635
);
2736
}
37+
38+
function test2() public returns (uint256 a, uint256 b) {
39+
assembly {
40+
tstore(REENTRANCY_GUARD_STORAGE, 1)
41+
sstore(REENTRANCY_GUARD_STORAGE, 134)
42+
a := sload(REENTRANCY_GUARD_STORAGE)
43+
b := tload(REENTRANCY_GUARD_STORAGE)
44+
}
45+
return (a, b);
46+
}
2847
}

src/codegen/dead_storage.rs

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use super::cfg::{BasicBlock, ControlFlowGraph, Instr};
44
use crate::codegen::Expression;
55
use crate::sema::ast::{Namespace, RetrieveType, Type};
6-
use solang_parser::pt::Loc;
6+
use solang_parser::pt::{Loc, StorageType};
77
use std::collections::{HashMap, HashSet};
88
use std::fmt;
99

@@ -51,6 +51,7 @@ enum Transfer {
5151
Store {
5252
def: Definition,
5353
expr: Option<Expression>,
54+
storage_type: Option<StorageType>,
5455
},
5556
}
5657

@@ -66,8 +67,12 @@ impl fmt::Display for Transfer {
6667
Transfer::Kill { var_no } => {
6768
write!(f, "Kill %{var_no}")
6869
}
69-
Transfer::Store { def, expr } => {
70-
write!(f, "Storage: {expr:?} at {def}")
70+
Transfer::Store {
71+
def,
72+
expr,
73+
storage_type,
74+
} => {
75+
write!(f, "Storage: {expr:?} at {def} (type: {storage_type:?})")
7176
}
7277
}
7378
}
@@ -76,7 +81,7 @@ impl fmt::Display for Transfer {
7681
#[derive(Clone, PartialEq, Eq)]
7782
struct ReachingDefs {
7883
vars: HashMap<usize, HashMap<Definition, Option<Expression>>>,
79-
stores: Vec<(Definition, Expression)>,
84+
stores: Vec<(Definition, Expression, Option<StorageType>)>,
8085
}
8186

8287
type BlockVars = HashMap<usize, Vec<ReachingDefs>>;
@@ -165,7 +170,11 @@ fn reaching_definitions(cfg: &mut ControlFlowGraph) -> (Vec<Vec<Vec<Transfer>>>,
165170

166171
// merge storage stores
167172
for store in &vars.stores {
168-
if !block_vars[0].stores.iter().any(|(def, _)| *def == store.0) {
173+
if !block_vars[0]
174+
.stores
175+
.iter()
176+
.any(|(def, _, _)| *def == store.0)
177+
{
169178
block_vars[0].stores.push(store.clone());
170179
changed = true;
171180
}
@@ -230,7 +239,11 @@ fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
230239
// possibly we should check if the function is pure/view and not clear storage references
231240
let mut v = set_var(res);
232241

233-
v.push(Transfer::Store { def, expr: None });
242+
v.push(Transfer::Store {
243+
def,
244+
expr: None,
245+
storage_type: None,
246+
});
234247

235248
v
236249
}
@@ -256,7 +269,11 @@ fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
256269
// A constructor/external call can call us back and modify storage
257270
vec![
258271
Transfer::Kill { var_no: *res },
259-
Transfer::Store { def, expr: None },
272+
Transfer::Store {
273+
def,
274+
expr: None,
275+
storage_type: None,
276+
},
260277
]
261278
}
262279
Instr::Store { dest, .. } => {
@@ -277,15 +294,29 @@ fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
277294
vec![
278295
Transfer::Kill { var_no: *res },
279296
Transfer::Kill { var_no: *success },
280-
Transfer::Store { def, expr: None },
297+
Transfer::Store {
298+
def,
299+
expr: None,
300+
storage_type: None,
301+
},
281302
]
282303
}
283-
Instr::SetStorageBytes { storage, .. }
284-
| Instr::ClearStorage { storage, .. }
285-
| Instr::SetStorage { storage, .. } => {
304+
Instr::SetStorageBytes { storage, .. } | Instr::ClearStorage { storage, .. } => {
286305
vec![Transfer::Store {
287306
def,
288307
expr: Some(storage.clone()),
308+
storage_type: None,
309+
}]
310+
}
311+
Instr::SetStorage {
312+
storage,
313+
storage_type,
314+
..
315+
} => {
316+
vec![Transfer::Store {
317+
def,
318+
expr: Some(storage.clone()),
319+
storage_type: storage_type.clone(),
289320
}]
290321
}
291322
Instr::PopStorage {
@@ -300,11 +331,16 @@ fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
300331
Transfer::Store {
301332
def,
302333
expr: Some(storage.clone()),
334+
storage_type: None,
303335
},
304336
]
305337
}
306338
Instr::Return { .. } => {
307-
vec![Transfer::Store { def, expr: None }]
339+
vec![Transfer::Store {
340+
def,
341+
expr: None,
342+
storage_type: None,
343+
}]
308344
}
309345
_ => Vec::new(),
310346
});
@@ -402,7 +438,11 @@ fn apply_transfers(
402438
vars.vars.insert(*var_no, v);
403439
}
404440
}
405-
Transfer::Store { def, expr } => {
441+
Transfer::Store {
442+
def,
443+
expr,
444+
storage_type,
445+
} => {
406446
// store to contract storage. This should kill any equal
407447
let mut eliminated_vars = Vec::new();
408448

@@ -440,7 +480,9 @@ fn apply_transfers(
440480
// all stores should are no longer reaching if they are clobbered by this store
441481
let mut eliminated_stores = Vec::new();
442482

443-
for (no, (def, storage)) in vars.stores.iter().enumerate() {
483+
for (no, (def, storage, store_storage_type)) in
484+
vars.stores.iter().enumerate()
485+
{
444486
let storage_vars = get_vars_at(def, block_vars);
445487

446488
if expression_compare(
@@ -451,6 +493,7 @@ fn apply_transfers(
451493
cfg,
452494
block_vars,
453495
) == ExpressionCmp::Equal
496+
&& storage_type == store_storage_type
454497
{
455498
eliminated_stores.push(no);
456499
}
@@ -460,7 +503,7 @@ fn apply_transfers(
460503
vars.stores.remove(no);
461504
}
462505

463-
vars.stores.push((*def, expr.clone()));
506+
vars.stores.push((*def, expr.clone(), storage_type.clone()));
464507
} else {
465508
// flush all reaching stores
466509
vars.stores.truncate(0);
@@ -495,7 +538,11 @@ pub fn dead_storage(cfg: &mut ControlFlowGraph, _ns: &mut Namespace) {
495538

496539
match &cfg.blocks[block_no].instr[instr_no] {
497540
Instr::LoadStorage {
498-
res, ty, storage, ..
541+
res,
542+
ty,
543+
storage,
544+
storage_type,
545+
..
499546
} => {
500547
// is there a definition which has the same storage expression
501548
let mut found = None;
@@ -519,6 +566,7 @@ pub fn dead_storage(cfg: &mut ControlFlowGraph, _ns: &mut Namespace) {
519566
) == ExpressionCmp::Equal
520567
&& storage_def.var_no != *res
521568
&& ty == storage_def.ty
569+
&& storage_type.as_ref() == storage_def.storage_type
522570
{
523571
found = Some(var_no);
524572
break;
@@ -538,11 +586,12 @@ pub fn dead_storage(cfg: &mut ControlFlowGraph, _ns: &mut Namespace) {
538586
},
539587
};
540588
} else {
541-
for (def, expr) in &vars.stores {
589+
for (def, expr, store_storage_type) in &vars.stores {
542590
let def_vars = get_vars_at(def, &block_vars);
543591

544592
if expression_compare(storage, vars, expr, &def_vars, cfg, &block_vars)
545593
!= ExpressionCmp::NotEqual
594+
&& storage_type == store_storage_type
546595
{
547596
if let Some(entry) = redundant_stores.get_mut(def) {
548597
*entry = false;
@@ -552,7 +601,7 @@ pub fn dead_storage(cfg: &mut ControlFlowGraph, _ns: &mut Namespace) {
552601
}
553602
}
554603
Instr::PushStorage { storage, .. } | Instr::PopStorage { storage, .. } => {
555-
for (def, expr) in &vars.stores {
604+
for (def, expr, _store_storage_type) in &vars.stores {
556605
let def_vars = get_vars_at(def, &block_vars);
557606

558607
if expression_compare(storage, vars, expr, &def_vars, cfg, &block_vars)
@@ -585,7 +634,7 @@ pub fn dead_storage(cfg: &mut ControlFlowGraph, _ns: &mut Namespace) {
585634
.iter()
586635
.any(|t| matches!(t, Transfer::Store { expr: None, .. }))
587636
{
588-
for (def, _) in &vars.stores {
637+
for (def, _, _) in &vars.stores {
589638
// insert new entry or override existing one
590639
redundant_stores.insert(*def, false);
591640
}
@@ -613,6 +662,7 @@ struct StorageDef<'a> {
613662
var_no: usize,
614663
slot: &'a Expression,
615664
ty: &'a Type,
665+
storage_type: Option<&'a StorageType>,
616666
}
617667

618668
fn get_storage_definition<'a>(
@@ -625,11 +675,16 @@ fn get_storage_definition<'a>(
625675
{
626676
match &cfg.blocks[*block_no].instr[*instr_no] {
627677
Instr::LoadStorage {
628-
storage, res, ty, ..
678+
storage,
679+
res,
680+
ty,
681+
storage_type,
682+
..
629683
} => Some(StorageDef {
630684
var_no: *res,
631685
slot: storage,
632686
ty,
687+
storage_type: storage_type.as_ref(),
633688
}),
634689
_ => None,
635690
}

src/codegen/expression.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ pub fn expression(
458458
let address_res = vartab.temp_anonymous(&Type::Contract(*constructor_contract));
459459
let success = ns
460460
.target
461-
.is_polkadot()
461+
.is_polkadot_stylus()
462462
.then(|| vartab.temp_name("success", &Type::Uint(32)));
463463
call_constructor(
464464
loc,
@@ -475,7 +475,7 @@ pub fn expression(
475475
cfg,
476476
opt,
477477
);
478-
if ns.target.is_polkadot() {
478+
if ns.target.is_polkadot_stylus() {
479479
polkadot::RetCodeCheckBuilder::default()
480480
.loc(*loc)
481481
.msg("contract creation failed")

src/codegen/yul/builtin.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
};
1919
use num_bigint::BigInt;
2020
use num_traits::{FromPrimitive, Zero};
21-
use solang_parser::pt;
21+
use solang_parser::pt::{self, StorageType};
2222

2323
impl Expression {
2424
fn to_number_literal(&self) -> Expression {
@@ -103,9 +103,6 @@ pub(crate) fn process_builtin(
103103
| YulBuiltInFunction::MStore
104104
| YulBuiltInFunction::MStore8
105105
| YulBuiltInFunction::MSize
106-
// Storage function: need to think about how to deal with pointer size and the size of chunk to load
107-
| YulBuiltInFunction::SStore
108-
| YulBuiltInFunction::SLoad
109106
// Calldata functions: the same problems with other memory functions
110107
| YulBuiltInFunction::CallDataLoad
111108
| YulBuiltInFunction::CallDataSize
@@ -135,8 +132,6 @@ pub(crate) fn process_builtin(
135132
| YulBuiltInFunction::Log2
136133
| YulBuiltInFunction::Log3
137134
| YulBuiltInFunction::Log4
138-
// origin is the same as tx.origin and is not implemented
139-
| YulBuiltInFunction::Origin
140135
| YulBuiltInFunction::PrevRandao
141136
=> {
142137
if ns.target != Target::EVM {
@@ -149,6 +144,47 @@ pub(crate) fn process_builtin(
149144
Expression::Poison
150145
}
151146

147+
YulBuiltInFunction::Origin => {
148+
Expression::Builtin { loc: *loc, tys: vec![Type::Address(false)], kind: Builtin::Origin, args: vec![] }
149+
}
150+
151+
YulBuiltInFunction::TLoad => {
152+
let slot = expression(&args[0], contract_no, ns, vartab, cfg, opt);
153+
let res = vartab.temp_anonymous(&Type::Uint(256));
154+
cfg.add(vartab, Instr::LoadStorage { res, ty: Type::Uint(256), storage: slot, storage_type: Some(StorageType::Temporary(None)) });
155+
Expression::Variable {
156+
loc: *loc,
157+
ty: Type::Uint(256),
158+
var_no: res,
159+
}
160+
}
161+
162+
YulBuiltInFunction::TStore => {
163+
let slot = expression(&args[0], contract_no, ns, vartab, cfg, opt);
164+
let value = expression(&args[1], contract_no, ns, vartab, cfg, opt);
165+
cfg.add(vartab, Instr::SetStorage { ty: Type::Uint(256), value, storage: slot, storage_type: Some(StorageType::Temporary(None)) });
166+
Expression::Poison
167+
}
168+
169+
170+
YulBuiltInFunction::SLoad => {
171+
let slot = expression(&args[0], contract_no, ns, vartab, cfg, opt);
172+
let res = vartab.temp_anonymous(&Type::Uint(256));
173+
cfg.add(vartab, Instr::LoadStorage { res, ty: Type::Uint(256), storage: slot, storage_type: Some(StorageType::Persistent(None)) });
174+
Expression::Variable {
175+
loc: *loc,
176+
ty: Type::Uint(256),
177+
var_no: res,
178+
}
179+
}
180+
181+
YulBuiltInFunction::SStore => {
182+
let slot = expression(&args[0], contract_no, ns, vartab, cfg, opt);
183+
let value = expression(&args[1], contract_no, ns, vartab, cfg, opt);
184+
cfg.add(vartab, Instr::SetStorage { ty: Type::Uint(256), value, storage: slot, storage_type: Some(StorageType::Persistent(None)) });
185+
Expression::Poison
186+
}
187+
152188
YulBuiltInFunction::Gas => {
153189
Expression::Builtin { loc: *loc, tys: vec![Type::Uint(64)], kind: Builtin::Gasleft, args: vec![] }
154190
}

0 commit comments

Comments
 (0)