Skip to content

Commit ea312a9

Browse files
authored
Merge pull request #162 from nevillegrech/storage-modeling
Storage modeling
2 parents 5439a26 + efcd56d commit ea312a9

File tree

3 files changed

+117
-14
lines changed

3 files changed

+117
-14
lines changed

clientlib/casts_shifts.dl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ CastedAndShiftedVar(originVar, toVar, shiftedBy, castedTo):-
163163
castedTo = strlen(unshiftedMask)/2 - 1,
164164
as(@shl_256(shiftBits, unshiftedMask), Value) = shiftedMask.
165165

166+
// Recursive case, when a variable is recasted after being shifted
167+
CastedAndShiftedVar(originVar, reCastedVar, shiftedBy, castedTo):-
168+
CastedAndShiftedVar(originVar, castedNShifted, shiftedBy, castedTo),
169+
ByteMaskOpKeepRange(castedNShifted, reCastedVar, [shiftedBy, shiftedBy + castedTo - 1]).
170+
166171
// Hack
167172
CastedAndShiftedVar(caller, caller, 0, 20):-
168173
CALLER(_, caller).

clientlib/storage_modeling/data_structures.dl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ DataStructure_Type($Array(parentCons), cat(type, "[]")):-
428428
!BytesOrStringLengthV2($Array(parentCons), _).
429429

430430
DataStructure_Type($Array(parentCons), "string"):-
431-
DataStructure_ValueOrElementType($Array(parentCons), _),
431+
IsDataStructureConstruct($Array(parentCons)),
432432
BytesOrStringLengthV2($Array(parentCons), _).
433433

434434
StorageVariable_Type(var, "uint256"):-
@@ -513,7 +513,7 @@ StorageStmtToIndexAndConstruct(store, "ACCESS", index, $TightlyPackedVariable(co
513513
StorageIndex_StorageConstruct(index, cons),
514514
Variable_StorageIndex(indexVar, index),
515515
StorageAccessOp(store, indexVar),
516-
(VarWrittenToBytesOfStorVarFinal(_, store, $Variable(cons), low, high); ConstWrittenToBytesOfStorVar(_, _, store, _, $Variable(cons), low, high)),
516+
(VarWrittenToBytesOfStorVarFinal(_, store, $Variable(cons), low, high); ConstWrittenToBytesOfStorVarProcessed(_, _, store, _, $Variable(cons), low, high); DeleteOfStructSlot(store, $Variable(cons))),
517517
ProcessedStorageVariable($Variable(cons), $TightlyPackedVariable(cons, low, high)).
518518

519519
.decl StorageOffset_Type(offset: Value, type: symbol)

clientlib/storage_modeling/tight_packing.dl

Lines changed: 110 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11

2+
// TODO: Add shifts to `castsAndShiftsFlows`!!!!!!!!!
3+
// Maybe they are not there for a reason?
4+
// Example: 4b053890c3ceb6c77358d624bd0c10bb: 0x6a
25
.init castsAndShiftsFlows = LocalFlowAnalysis
36
castsAndShiftsFlows.TransferStmt(stmt):-
47
Statement_Opcode(stmt, "OR").
@@ -190,18 +193,34 @@ DEBUG_OUTPUT(SuccessfulMergedStorageModeling)
190193
AnyLoadStoreStorVarBytes(stmt, storVar, low, high):-
191194
VarHoldsBytesOfStorVarFinal(_, stmt, storVar, low, high);
192195
VarWrittenToBytesOfStorVar(_, stmt, storVar, low, high);
193-
// Will need to handle these differently because in optimized code a single constant can initialize many vars
194-
ConstWrittenToBytesOfStorVar(_, _, stmt, _, storVar, low, high).
196+
ConstWrittenToUnreadBytesOfStorVar(_, stmt, _, storVar, low, high).
197+
198+
// rare case, a variable is never read directly, but only stored
199+
// try to capture it with 2 simple rules
200+
VarWrittenToBytesOfStorVar(var, store, construct, 0, 31):-
201+
SSTOREToConstruct(store, construct, var),
202+
!Variable_Value(var, _),
203+
!LowBytesMaskOp(_, var, _),
204+
!SLOADOfConstruct(_, construct, _).
195205

196-
AnyLoadStoreStorVarBytes(store, construct, 0, 31):-
197-
SSTOREToConstruct(store, construct, _),
206+
// This pattern will be used when an array is shortened (replaced with a smaller one)
207+
// in via-ir code
208+
VarWrittenToBytesOfStorVar(var, store, construct, 0, numOfBytes - 1):-
209+
SSTOREToConstruct(store, construct, var),
210+
LowBytesMaskOp(_, var, numOfBytes),
211+
!Variable_Value(var, _),
212+
!SLOADOfConstruct(_, construct, _).
213+
214+
// Added for completeness to revisit
215+
ConstWrittenToBytesOfStorVar(var, val, store, store, construct, 0, 31):-
216+
SSTOREToConstruct(store, construct, var),
217+
Variable_Value(var, val),
198218
!SLOADOfConstruct(_, construct, _).
199219

200220
FailedMergedStorageModelingReason(storVar, stmt, stmt2, [low, high], [otherLow, otherHigh]),
201221
FailedMergedStorageModeling(storVar):-
202222
AnyLoadStoreStorVarBytes(stmt, storVar, low, high),
203223
AnyLoadStoreStorVarBytes(stmt2, storVar, otherLow, otherHigh), otherLow = otherLow, otherHigh = otherHigh, // NOWARN
204-
!ArrayIdToStorageIndex(as(storVar, Value), _),
205224
(low != otherLow ; high != otherHigh),
206225
( (low < otherLow , otherLow < high) ; (low < otherHigh, otherHigh < high) ).
207226

@@ -215,14 +234,14 @@ FailedMergedStorageModeling(storVar):-
215234
!UselessSLOAD(stmt),
216235
!AnyLoadStoreStorVarBytes(stmt, storVar, _, _),
217236
!BytesOfStorVarKept(_, stmt, storVar, _, _),
218-
!ConstWrittenToBytesOfStorVar(_, _, _, stmt, storVar, _, _),
219-
!ArrayIdToStorageIndex(as(storVar, Value), _).
237+
!DeleteOfStructSlot(stmt, storVar),
238+
!ConstWrittenToBytesOfStorVar(_, _, stmt, _, storVar, _, _),
239+
!ConstWrittenToBytesOfStorVar(_, _, _, stmt, storVar, _, _).
220240

221241

222242
SuccessfulMergedStorageModeling(storVar):-
223243
AnyLoadStoreStorVarBytes(_, storVar, _, _),
224-
!FailedMergedStorageModeling(storVar),
225-
!ArrayIdToStorageIndex(as(storVar, Value), _).
244+
!FailedMergedStorageModeling(storVar).
226245

227246
/**
228247
Models complex expressions of many shifted and masked vars to update a single storage slot.
@@ -262,6 +281,12 @@ DEBUG_OUTPUT(VarWrittenToBytesOfStorVar)
262281
.decl ConstWrittenToBytesOfStorVar(constVar:Variable, const:Value, store:Statement, load:Statement, storVar:StorageConstruct, byteLow:number, byteHigh:number)
263282
DEBUG_OUTPUT(ConstWrittenToBytesOfStorVar)
264283

284+
/**
285+
Results of ConstWrittenToBytesOfStorVar processed after identifying the variables
286+
*/
287+
.decl ConstWrittenToBytesOfStorVarProcessed(constVar:Variable, const:Value, store:Statement, load:Statement, storVar:StorageConstruct, byteLow:number, byteHigh:number)
288+
DEBUG_OUTPUT(ConstWrittenToBytesOfStorVarProcessed)
289+
265290
ProbablyPartialStorageUpdatePattern(fromVar, toVar, "VAR", writtenVar, byteLow, byteLow + castedTo - 1):-
266291
CastedAndShiftedVar(writtenVar, castedNShifted, byteLow, castedTo),
267292
(OR(_, castedNShifted, fromVar, toVar) ; OR(_, fromVar, castedNShifted, toVar)),
@@ -378,6 +403,16 @@ ConstWrittenToBytesOfStorVar("0xlala", as(writtenConst, Value), store, "lolo", c
378403
ProbablyPartialStorageUpdateSequenceWrite(storedVar, "CONST", writtenConst, byteLow, byteHigh),
379404
!ProbablyPartialStorageUpdateSequenceWrite(storedVar, "VAR", _, byteLow, _). // prefer var inference if they start from the same byte
380405

406+
/**
407+
Needs special handling because in via-ir deleted storage structs have all their bytes zeroed out.
408+
Before the unused bytes would be kept.
409+
*/
410+
.decl DeleteOfStructSlot(store: Statement, construct: StorageConstruct)
411+
DEBUG_OUTPUT(DeleteOfStructSlot)
412+
DeleteOfStructSlot(store, construct):-
413+
SSTOREToConstruct(store, construct, storedVar),
414+
Variable_Value(storedVar, "0x0").
415+
381416
.decl BytesOfStorVarKept(store: Statement, load: Statement, construct: StorageConstruct, keepByteLow: number, keepByteHigh: number)
382417

383418
BytesOfStorVarKept(store, load, construct, keepByteLow, keepByteHigh):-
@@ -451,6 +486,43 @@ ConstWrittenToBytesOfStorVar("0xNoVar", "0x0", store, load, construct, maskByteL
451486
ByteMaskOpMaskRange(originVar, storedVar, [maskByteLow, maskByteHigh]),
452487
SSTOREToConstruct(store, construct, storedVar).
453488

489+
/**
490+
byte of storage variable is never read ConstWrittenToByteOfStorVar that write to bytes that are never read
491+
*/
492+
.decl ConstWrittenToUnreadByteOfStorVar(const: Value, store: Statement, load: Statement, storVar: StorageConstruct, byteLow: number, writeByte: number)
493+
DEBUG_OUTPUT(ConstWrittenToUnreadByteOfStorVar)
494+
495+
/**
496+
Subset of ConstWrittenToBytesOfStorVar that write to bytes that are never read
497+
Will first identify individual bytes and then group them together
498+
*/
499+
.decl ConstWrittenToUnreadBytesOfStorVar(const: Value, store: Statement, load: Statement, storVar: StorageConstruct, byteLow: number, byteHigh: number)
500+
DEBUG_OUTPUT(ConstWrittenToUnreadBytesOfStorVar)
501+
502+
ConstWrittenToUnreadByteOfStorVar(const, store, load, storVar, byteLow, writeByte):-
503+
ConstWrittenToBytesOfStorVar(_, const, store, load, storVar, byteLow, byteHigh),
504+
writeByte = range(byteLow, byteHigh + 1, 1),
505+
!ByteOfStorVarReadOfWritten(storVar, writeByte).
506+
507+
ConstWrittenToUnreadBytesOfStorVar(newVal, store, load, storVar, byteLow, byteHigh):-
508+
ConstWrittenToUnreadByteOfStorVar(const, store, load, storVar, originalByteLow, byteLow),
509+
!ConstWrittenToUnreadByteOfStorVar(const, store, load, storVar, originalByteLow, byteLow - 1),
510+
ConstWrittenToUnreadByteOfStorVar(const, store, load, storVar, originalByteLow, byteHigh),
511+
!ConstWrittenToUnreadByteOfStorVar(const, store, load, storVar, originalByteLow, byteHigh + 1),
512+
1 + byteHigh - byteLow = count : {ConstWrittenToUnreadByteOfStorVar(const, store, load, storVar, originalByteLow, k), byteLow <= k, k <= byteHigh},
513+
excessRightBytes = byteLow - originalByteLow,
514+
Mask_Length(mask, byteHigh - byteLow + 1),
515+
ValueIsByteMask(mask),
516+
newVal = as(@and_256(@shr_256(@number_to_hex(excessRightBytes*8), const), mask), Value).
517+
518+
519+
// Helper
520+
.decl ByteOfStorVarReadOfWritten(storVar: StorageConstruct, byte: number)
521+
ByteOfStorVarReadOfWritten(storVar, byte):-
522+
(VarHoldsBytesOfStorVarFinal(_, _, storVar, varByteLow, varByteHigh);
523+
VarWrittenToBytesOfStorVar(_, _, storVar, varByteLow, varByteHigh)),
524+
byte = range(varByteLow, varByteHigh + 1, 1).
525+
454526
/**
455527
HACK (?) HACK (?) HACK (?)
456528
If the variable that is being stored on the update of a merged storage var
@@ -545,7 +617,7 @@ StoreGlobalVariable(store, v, writtenVar):-
545617
StoreGlobalVariable(store, v, constVar):-
546618
SuccessfulMergedStorageModeling($Variable($Constant(storVar))),
547619
SSTOREToConst(_, storVar, _), // ensure it's a global variable
548-
ConstWrittenToBytesOfStorVar(constVar, _, store, _, $Variable($Constant(storVar)), byteLow, byteHigh),
620+
ConstWrittenToBytesOfStorVarProcessed(constVar, _, store, _, $Variable($Constant(storVar)), byteLow, byteHigh),
549621
v = MERGED_STORAGE_VAR(storVar, byteLow, byteHigh).
550622

551623

@@ -562,7 +634,7 @@ ProcessedStorageVariable(storageVar, storageVar):-
562634
ProcessedStorageVariable(construct, construct):-
563635
SuccessfulMergedStorageModeling(construct),
564636
(
565-
ConstWrittenToBytesOfStorVar(_, _, _, _, construct, 0, 31);
637+
// ConstWrittenToBytesOfStorVar(_, _, _, _, construct, 0, 31);
566638
VarWrittenToBytesOfStorVarFinal(_, _, construct, 0, 31);
567639
VarHoldsBytesOfStorVarFinal(_, _, construct, 0, 31)
568640
).
@@ -571,7 +643,7 @@ ProcessedStorageVariable(storageVar, $TightlyPackedVariable(parentCons, byteLow,
571643
SuccessfulMergedStorageModeling(storageVar),
572644
storageVar = $Variable(parentCons),
573645
(
574-
ConstWrittenToBytesOfStorVar(_, _, _, _, storageVar, byteLow, byteHigh);
646+
ConstWrittenToUnreadBytesOfStorVar(_, _, _, storageVar, byteLow, byteHigh);
575647
VarWrittenToBytesOfStorVarFinal(_, _, storageVar, byteLow, byteHigh);
576648
VarHoldsBytesOfStorVarFinal(_, _, storageVar, byteLow, byteHigh)
577649
),
@@ -588,6 +660,32 @@ StorageVariablePacksNVars(storageVar, numberOfVars):-
588660
numberOfVars = count : ProcessedStorageVariable(storageVar, _),
589661
numberOfVars > 1.
590662

663+
ConstWrittenToBytesOfStorVarProcessed(constVar, const, store, load, $Variable(parentCons), byteLow, byteHigh):-
664+
ConstWrittenToBytesOfStorVar(constVar, const, store, load, $Variable(parentCons), byteLow, byteHigh),
665+
ProcessedStorageVariable($Variable(parentCons), $TightlyPackedVariable(parentCons, byteLow, byteHigh)).
666+
667+
ConstWrittenToBytesOfStorVarProcessed(constVar, const, store, load, $Variable(parentCons), byteLow, actualByteHigh):-
668+
ConstWrittenToBytesOfStorVar(constVar, const, store, load, $Variable(parentCons), byteLow, byteHigh),
669+
ProcessedStorageVariable($Variable(parentCons), $TightlyPackedVariable(parentCons, byteLow, actualByteHigh)),
670+
actualByteHigh > byteHigh.
671+
672+
ConstWrittenToBytesOfStorVarProcessed("0xNoVar", as(@and_256(const, mask), Value), store, load, $Variable(parentCons), byteLow, actualByteHigh):-
673+
ConstWrittenToBytesOfStorVar(_, const, store, load, $Variable(parentCons), byteLow, byteHigh),
674+
ProcessedStorageVariable($Variable(parentCons), $TightlyPackedVariable(parentCons, byteLow, actualByteHigh)),
675+
actualByteHigh < byteHigh,
676+
Mask_Length(mask, byteHigh - actualByteHigh),
677+
ValueIsByteMask(mask).
678+
679+
ConstWrittenToBytesOfStorVarProcessed("0xNoVar", newVal, store, load, $Variable(parentCons), actualByteLow, actualByteHigh):-
680+
ConstWrittenToBytesOfStorVar(_, const, store, load, $Variable(parentCons), writeByteLow, writeByteHigh),
681+
ProcessedStorageVariable($Variable(parentCons), $TightlyPackedVariable(parentCons, actualByteLow, actualByteHigh)),
682+
writeByteLow <= actualByteLow,
683+
actualByteHigh <= writeByteHigh,
684+
excessRightBytes = actualByteLow - writeByteLow,
685+
Mask_Length(mask, actualByteHigh - actualByteLow + 1),
686+
ValueIsByteMask(mask),
687+
newVal = as(@and_256(@shr_256(@number_to_hex(excessRightBytes*8), const), mask), Value).
688+
591689
/**
592690
Basic type inference
593691
Hacky for now just to print the correct uintX or address if nessesary.

0 commit comments

Comments
 (0)