@@ -16,16 +16,17 @@ use mir::ir::{AddressSpaceKind, IntrinsicOp, Place, SyntheticValue};
1616use mir:: { MirModule , layout, layout:: TargetDataLayout , lower_module} ;
1717use num_bigint:: BigUint ;
1818use rustc_hash:: { FxHashMap , FxHashSet } ;
19+ use smallvec1:: SmallVec ;
1920use sonatina_ir:: {
2021 BlockId , I256 , Module , Signature , Type , ValueId ,
2122 builder:: { ModuleBuilder , Variable } ,
2223 func_cursor:: InstInserter ,
2324 inst:: {
2425 arith:: { Add , Mul , Neg , Shl , Shr , Sub } ,
25- cast:: { Sext , Trunc , Zext } ,
26+ cast:: { IntToPtr , PtrToInt , Sext , Trunc , Zext } ,
2627 cmp:: { Eq , Gt , IsZero , Lt , Ne } ,
2728 control_flow:: { Br , Call , Jump , Return } ,
28- data:: { Mload , Mstore , SymAddr , SymSize , SymbolRef } ,
29+ data:: { Gep , Mload , Mstore , SymAddr , SymSize , SymbolRef } ,
2930 evm:: {
3031 EvmAddress , EvmBaseFee , EvmBlockHash , EvmCall , EvmCallValue , EvmCalldataCopy ,
3132 EvmCalldataLoad , EvmCalldataSize , EvmCaller , EvmChainId , EvmCodeCopy , EvmCodeSize ,
@@ -261,6 +262,10 @@ struct ModuleLowerer<'db, 'a> {
261262 ///
262263 /// These entry functions emit `evm_stop` instead of internal `Return`.
263264 entry_func_idxs : FxHashSet < usize > ,
265+ /// Cache for Fe type → sonatina type mapping (GEP support).
266+ gep_type_cache : FxHashMap < String , Option < Type > > ,
267+ /// Counter for generating unique sonatina struct type names.
268+ gep_name_counter : usize ,
264269}
265270
266271impl < ' db , ' a > ModuleLowerer < ' db , ' a > {
@@ -282,6 +287,8 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
282287 returns_value_map : FxHashMap :: default ( ) ,
283288 runtime_param_masks : FxHashMap :: default ( ) ,
284289 entry_func_idxs : FxHashSet :: default ( ) ,
290+ gep_type_cache : FxHashMap :: default ( ) ,
291+ gep_name_counter : 0 ,
285292 }
286293 }
287294
@@ -840,7 +847,7 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
840847 /// If `is_entry` is true, this is the entry function executed directly by the EVM.
841848 /// Entry functions emit `evm_stop` instead of internal `Return` for their terminators.
842849 fn lower_function (
843- & self ,
850+ & mut self ,
844851 func_ref : FuncRef ,
845852 func : & mir:: MirFunction < ' db > ,
846853 is_entry : bool ,
@@ -938,6 +945,8 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
938945 block_map : & block_map,
939946 is,
940947 is_entry,
948+ gep_type_cache : & mut self . gep_type_cache ,
949+ gep_name_counter : & mut self . gep_name_counter ,
941950 } ;
942951
943952 for ( idx, block) in ctx. body . blocks . iter ( ) . enumerate ( ) {
@@ -972,6 +981,10 @@ struct LowerCtx<'a, 'db, C: sonatina_ir::func_cursor::FuncCursor> {
972981 block_map : & ' a FxHashMap < mir:: BasicBlockId , BlockId > ,
973982 is : & ' a sonatina_ir:: inst:: evm:: inst_set:: EvmInstSet ,
974983 is_entry : bool ,
984+ /// Cache for Fe type → sonatina type mapping (GEP support).
985+ gep_type_cache : & ' a mut FxHashMap < String , Option < Type > > ,
986+ /// Counter for generating unique sonatina struct type names.
987+ gep_name_counter : & ' a mut usize ,
975988}
976989
977990/// Lower a MIR instruction.
@@ -2090,6 +2103,140 @@ fn apply_to_word<'db, C: sonatina_ir::func_cursor::FuncCursor>(
20902103 }
20912104}
20922105
2106+ /// Maps a Fe type to a sonatina struct/array type for GEP-based addressing.
2107+ ///
2108+ /// Returns `Some(Type)` for types that can be represented as sonatina compound types
2109+ /// (structs, tuples, arrays of such). Returns `None` for types where we should fall
2110+ /// back to manual offset arithmetic (enums, zero-sized types, plain scalars).
2111+ ///
2112+ /// Uses a cache to avoid creating duplicate struct type definitions. The cache is keyed
2113+ /// by the Fe `TyId` debug representation (salsa-interned, so stable within a session).
2114+ fn fe_ty_to_sonatina < ' db , C : sonatina_ir:: func_cursor:: FuncCursor > (
2115+ fb : & mut sonatina_ir:: builder:: FunctionBuilder < C > ,
2116+ db : & ' db DriverDataBase ,
2117+ target_layout : & TargetDataLayout ,
2118+ ty : hir:: analysis:: ty:: ty_def:: TyId < ' db > ,
2119+ cache : & mut FxHashMap < String , Option < Type > > ,
2120+ name_counter : & mut usize ,
2121+ ) -> Option < Type > {
2122+ let cache_key = format ! ( "{ty:?}" ) ;
2123+ if let Some ( cached) = cache. get ( & cache_key) {
2124+ return * cached;
2125+ }
2126+
2127+ let result = fe_ty_to_sonatina_inner ( fb, db, target_layout, ty, cache, name_counter) ;
2128+ cache. insert ( cache_key, result) ;
2129+ result
2130+ }
2131+
2132+ fn fe_ty_to_sonatina_inner < ' db , C : sonatina_ir:: func_cursor:: FuncCursor > (
2133+ fb : & mut sonatina_ir:: builder:: FunctionBuilder < C > ,
2134+ db : & ' db DriverDataBase ,
2135+ target_layout : & TargetDataLayout ,
2136+ ty : hir:: analysis:: ty:: ty_def:: TyId < ' db > ,
2137+ cache : & mut FxHashMap < String , Option < Type > > ,
2138+ name_counter : & mut usize ,
2139+ ) -> Option < Type > {
2140+ if is_erased_runtime_ty ( db, target_layout, ty) {
2141+ return Some ( Type :: Unit ) ;
2142+ }
2143+
2144+ let base_ty = ty. base_ty ( db) ;
2145+ match base_ty. data ( db) {
2146+ TyData :: TyBase ( TyBase :: Prim ( prim) ) => match prim {
2147+ // Scalars: all map to I256 on EVM
2148+ PrimTy :: Bool
2149+ | PrimTy :: U8
2150+ | PrimTy :: I8
2151+ | PrimTy :: U16
2152+ | PrimTy :: I16
2153+ | PrimTy :: U32
2154+ | PrimTy :: I32
2155+ | PrimTy :: U64
2156+ | PrimTy :: I64
2157+ | PrimTy :: U128
2158+ | PrimTy :: I128
2159+ | PrimTy :: U256
2160+ | PrimTy :: I256
2161+ | PrimTy :: Usize
2162+ | PrimTy :: Isize
2163+ | PrimTy :: Ptr => Some ( Type :: I256 ) ,
2164+ PrimTy :: String => None ,
2165+ PrimTy :: Tuple ( _) => {
2166+ let field_tys = ty. field_types ( db) ;
2167+ if field_tys. is_empty ( ) {
2168+ return Some ( Type :: Unit ) ;
2169+ }
2170+ let mut sonatina_fields = Vec :: with_capacity ( field_tys. len ( ) ) ;
2171+ for ft in & field_tys {
2172+ sonatina_fields. push ( fe_ty_to_sonatina (
2173+ fb,
2174+ db,
2175+ target_layout,
2176+ * ft,
2177+ cache,
2178+ name_counter,
2179+ ) ?) ;
2180+ }
2181+ let id = * name_counter;
2182+ * name_counter += 1 ;
2183+ Some ( fb. declare_struct_type ( & format ! ( "__fe_tuple_{id}" ) , & sonatina_fields, false ) )
2184+ }
2185+ PrimTy :: Array => {
2186+ let elem_ty = layout:: array_elem_ty ( db, ty) ?;
2187+ let len = layout:: array_len ( db, ty) ?;
2188+ let sonatina_elem =
2189+ fe_ty_to_sonatina ( fb, db, target_layout, elem_ty, cache, name_counter) ?;
2190+ Some ( fb. declare_array_type ( sonatina_elem, len) )
2191+ }
2192+ } ,
2193+ TyData :: TyBase ( TyBase :: Adt ( adt_def) ) => {
2194+ match adt_def. adt_ref ( db) {
2195+ AdtRef :: Struct ( _) => {
2196+ let field_tys = ty. field_types ( db) ;
2197+ let mut sonatina_fields = Vec :: with_capacity ( field_tys. len ( ) ) ;
2198+ for ft in & field_tys {
2199+ sonatina_fields. push ( fe_ty_to_sonatina (
2200+ fb,
2201+ db,
2202+ target_layout,
2203+ * ft,
2204+ cache,
2205+ name_counter,
2206+ ) ?) ;
2207+ }
2208+ let name = adt_def
2209+ . adt_ref ( db)
2210+ . name ( db)
2211+ . map ( |id| id. data ( db) . to_string ( ) )
2212+ . unwrap_or_else ( || "anon" . to_string ( ) ) ;
2213+ let id = * name_counter;
2214+ * name_counter += 1 ;
2215+ Some ( fb. declare_struct_type (
2216+ & format ! ( "__fe_{name}_{id}" ) ,
2217+ & sonatina_fields,
2218+ false ,
2219+ ) )
2220+ }
2221+ // Enums: fall back to manual arithmetic
2222+ AdtRef :: Enum ( _) => None ,
2223+ }
2224+ }
2225+ TyData :: TyBase ( TyBase :: Contract ( _) ) | TyData :: TyBase ( TyBase :: Func ( _) ) => Some ( Type :: Unit ) ,
2226+ _ => None ,
2227+ }
2228+ }
2229+
2230+ /// Checks whether a projection chain is eligible for GEP-based addressing.
2231+ ///
2232+ /// Returns true when all projections are Field or Index (no VariantField, Discriminant, or Deref).
2233+ fn projections_eligible_for_gep ( place : & Place < ' _ > ) -> bool {
2234+ place
2235+ . projection
2236+ . iter ( )
2237+ . all ( |p| matches ! ( p, Projection :: Field ( _) | Projection :: Index ( _) ) )
2238+ }
2239+
20932240/// Computes the address for a place by walking the projection path.
20942241///
20952242/// For memory, computes byte offsets. For storage, computes slot offsets.
@@ -2098,24 +2245,170 @@ fn lower_place_address<'db, C: sonatina_ir::func_cursor::FuncCursor>(
20982245 ctx : & mut LowerCtx < ' _ , ' db , C > ,
20992246 place : & Place < ' db > ,
21002247) -> Result < ValueId , LowerError > {
2101- let mut base_val = lower_value ( ctx, place. base ) ?;
2248+ let base_val = lower_value ( ctx, place. base ) ?;
21022249
21032250 if place. projection . is_empty ( ) {
21042251 return Ok ( base_val) ;
21052252 }
21062253
21072254 // Get the base value's type to navigate projections
21082255 let base_value = & ctx. body . values [ place. base . index ( ) ] ;
2109- let mut current_ty = base_value. ty ;
2256+ let current_ty = base_value. ty ;
21102257 if is_erased_runtime_ty ( ctx. db , ctx. target_layout , current_ty) {
21112258 return Ok ( base_val) ;
21122259 }
2113- let mut total_offset : usize = 0 ;
2260+
21142261 let is_slot_addressed = matches ! (
21152262 ctx. body. place_address_space( place) ,
21162263 AddressSpaceKind :: Storage | AddressSpaceKind :: TransientStorage
21172264 ) ;
21182265
2266+ // Use GEP for memory-addressed places where all projections are Field or Index
2267+ if !is_slot_addressed
2268+ && projections_eligible_for_gep ( place)
2269+ && let Some ( sonatina_ty) = fe_ty_to_sonatina (
2270+ ctx. fb ,
2271+ ctx. db ,
2272+ ctx. target_layout ,
2273+ current_ty,
2274+ ctx. gep_type_cache ,
2275+ ctx. gep_name_counter ,
2276+ )
2277+ {
2278+ return lower_place_address_gep ( ctx, place, base_val, current_ty, sonatina_ty) ;
2279+ }
2280+
2281+ // Fall back to manual offset arithmetic
2282+ lower_place_address_arithmetic ( ctx, place, base_val, current_ty, is_slot_addressed)
2283+ }
2284+
2285+ /// GEP-based place address computation for memory-addressed struct/array paths.
2286+ fn lower_place_address_gep < ' db , C : sonatina_ir:: func_cursor:: FuncCursor > (
2287+ ctx : & mut LowerCtx < ' _ , ' db , C > ,
2288+ place : & Place < ' db > ,
2289+ base_val : ValueId ,
2290+ base_fe_ty : hir:: analysis:: ty:: ty_def:: TyId < ' db > ,
2291+ base_sonatina_ty : Type ,
2292+ ) -> Result < ValueId , LowerError > {
2293+ let ptr_ty = ctx. fb . ptr_type ( base_sonatina_ty) ;
2294+
2295+ // IntToPtr: cast I256 base address to typed pointer
2296+ let typed_ptr = ctx
2297+ . fb
2298+ . insert_inst ( IntToPtr :: new ( ctx. is , base_val, ptr_ty) , ptr_ty) ;
2299+
2300+ // Build GEP index list, tracking types through the chain
2301+ let mut gep_values: SmallVec < [ ValueId ; 8 ] > = SmallVec :: new ( ) ;
2302+ gep_values. push ( typed_ptr) ;
2303+
2304+ // Initial dereference index (standard GEP convention: index 0 dereferences the pointer)
2305+ let zero = ctx. fb . make_imm_value ( I256 :: zero ( ) ) ;
2306+ gep_values. push ( zero) ;
2307+
2308+ let mut current_fe_ty = base_fe_ty;
2309+ let mut current_sonatina_ty = base_sonatina_ty;
2310+
2311+ for proj in place. projection . iter ( ) {
2312+ match proj {
2313+ Projection :: Field ( field_idx) => {
2314+ let idx_val = ctx. fb . make_imm_value ( I256 :: from ( * field_idx as u64 ) ) ;
2315+ gep_values. push ( idx_val) ;
2316+
2317+ // Navigate Fe type
2318+ let field_types = current_fe_ty. field_types ( ctx. db ) ;
2319+ current_fe_ty = * field_types. get ( * field_idx) . ok_or_else ( || {
2320+ LowerError :: Unsupported ( format ! ( "gep: field {field_idx} out of bounds" ) )
2321+ } ) ?;
2322+
2323+ // Navigate sonatina type to the field's type
2324+ current_sonatina_ty =
2325+ sonatina_struct_field_ty ( ctx, current_sonatina_ty, * field_idx) ?;
2326+ }
2327+ Projection :: Index ( idx_source) => {
2328+ let idx_val = match idx_source {
2329+ IndexSource :: Constant ( idx) => ctx. fb . make_imm_value ( I256 :: from ( * idx as u64 ) ) ,
2330+ IndexSource :: Dynamic ( value_id) => lower_value ( ctx, * value_id) ?,
2331+ } ;
2332+ gep_values. push ( idx_val) ;
2333+
2334+ // Navigate Fe type
2335+ current_fe_ty = layout:: array_elem_ty ( ctx. db , current_fe_ty) . ok_or_else ( || {
2336+ LowerError :: Unsupported ( "gep: array index on non-array type" . to_string ( ) )
2337+ } ) ?;
2338+
2339+ // Navigate sonatina type to array element
2340+ current_sonatina_ty = sonatina_array_elem_ty ( ctx, current_sonatina_ty) ?;
2341+ }
2342+ _ => unreachable ! ( "projections_eligible_for_gep ensures only Field/Index" ) ,
2343+ }
2344+ }
2345+
2346+ // The GEP result is a pointer to the final element type
2347+ let result_ptr_ty = ctx. fb . ptr_type ( current_sonatina_ty) ;
2348+ let gep_result = ctx
2349+ . fb
2350+ . insert_inst ( Gep :: new ( ctx. is , gep_values) , result_ptr_ty) ;
2351+
2352+ // PtrToInt: cast back to I256 for mload/mstore
2353+ let result = ctx
2354+ . fb
2355+ . insert_inst ( PtrToInt :: new ( ctx. is , gep_result, Type :: I256 ) , Type :: I256 ) ;
2356+
2357+ Ok ( result)
2358+ }
2359+
2360+ /// Resolves the sonatina type of a struct field by index.
2361+ fn sonatina_struct_field_ty < C : sonatina_ir:: func_cursor:: FuncCursor > (
2362+ ctx : & mut LowerCtx < ' _ , ' _ , C > ,
2363+ struct_ty : Type ,
2364+ field_idx : usize ,
2365+ ) -> Result < Type , LowerError > {
2366+ let fields = ctx
2367+ . fb
2368+ . module_builder
2369+ . ctx
2370+ . with_ty_store ( |s| s. struct_def ( struct_ty) . map ( |sd| sd. fields . clone ( ) ) ) ;
2371+ match fields {
2372+ Some ( f) => f. get ( field_idx) . copied ( ) . ok_or_else ( || {
2373+ LowerError :: Internal ( format ! (
2374+ "gep: sonatina struct field {field_idx} out of bounds"
2375+ ) )
2376+ } ) ,
2377+ None => Err ( LowerError :: Internal (
2378+ "gep: expected sonatina struct type for Field projection" . to_string ( ) ,
2379+ ) ) ,
2380+ }
2381+ }
2382+
2383+ /// Resolves the sonatina element type of an array type.
2384+ fn sonatina_array_elem_ty < C : sonatina_ir:: func_cursor:: FuncCursor > (
2385+ ctx : & mut LowerCtx < ' _ , ' _ , C > ,
2386+ array_ty : Type ,
2387+ ) -> Result < Type , LowerError > {
2388+ let elem = ctx
2389+ . fb
2390+ . module_builder
2391+ . ctx
2392+ . with_ty_store ( |s| s. array_def ( array_ty) . map ( |( elem, _len) | elem) ) ;
2393+ match elem {
2394+ Some ( e) => Ok ( e) ,
2395+ None => Err ( LowerError :: Internal (
2396+ "gep: expected sonatina array type for Index projection" . to_string ( ) ,
2397+ ) ) ,
2398+ }
2399+ }
2400+
2401+ /// Manual offset arithmetic path for place address computation.
2402+ /// Used for storage-addressed places and any memory path with enum projections.
2403+ fn lower_place_address_arithmetic < ' db , C : sonatina_ir:: func_cursor:: FuncCursor > (
2404+ ctx : & mut LowerCtx < ' _ , ' db , C > ,
2405+ place : & Place < ' db > ,
2406+ mut base_val : ValueId ,
2407+ mut current_ty : hir:: analysis:: ty:: ty_def:: TyId < ' db > ,
2408+ is_slot_addressed : bool ,
2409+ ) -> Result < ValueId , LowerError > {
2410+ let mut total_offset: usize = 0 ;
2411+
21192412 for proj in place. projection . iter ( ) {
21202413 match proj {
21212414 Projection :: Field ( field_idx) => {
0 commit comments