90
90
//! - Return v1 in memory at `[P+8]`.
91
91
//! - Return v0 in memory at `[P+16]`.
92
92
93
+ use crate :: binemit:: Stackmap ;
93
94
use crate :: ir;
94
95
use crate :: ir:: types;
95
96
use crate :: ir:: types:: * ;
96
97
use crate :: ir:: { ArgumentExtension , StackSlot } ;
97
98
use crate :: isa;
98
- use crate :: isa:: aarch64:: { inst:: * , lower:: ty_bits} ;
99
+ use crate :: isa:: aarch64:: { inst:: EmitState , inst :: * , lower:: ty_bits} ;
99
100
use crate :: machinst:: * ;
100
101
use crate :: settings;
101
102
use crate :: { CodegenError , CodegenResult } ;
@@ -372,7 +373,10 @@ pub struct AArch64ABIBody {
372
373
clobbered : Set < Writable < RealReg > > ,
373
374
/// Total number of spillslots, from regalloc.
374
375
spillslots : Option < usize > ,
375
- /// Total frame size.
376
+ /// "Total frame size", as defined by "distance between FP and nominal-SP".
377
+ /// Some items are pushed below nominal SP, so the function may actually use
378
+ /// more stack than this would otherwise imply. It is simply the initial
379
+ /// frame/allocation size needed for stackslots and spillslots.
376
380
total_frame_size : Option < u32 > ,
377
381
/// The register holding the return-area pointer, if needed.
378
382
ret_area_ptr : Option < Writable < Reg > > ,
@@ -400,6 +404,8 @@ fn in_int_reg(ty: ir::Type) -> bool {
400
404
match ty {
401
405
types:: I8 | types:: I16 | types:: I32 | types:: I64 => true ,
402
406
types:: B1 | types:: B8 | types:: B16 | types:: B32 | types:: B64 => true ,
407
+ types:: R64 => true ,
408
+ types:: R32 => panic ! ( "Unexpected 32-bit reference on a 64-bit platform!" ) ,
403
409
_ => false ,
404
410
}
405
411
}
@@ -653,12 +659,12 @@ fn load_stack(mem: MemArg, into_reg: Writable<Reg>, ty: Type) -> Inst {
653
659
mem,
654
660
srcloc : None ,
655
661
} ,
656
- types:: B32 | types:: I32 => Inst :: ULoad32 {
662
+ types:: B32 | types:: I32 | types :: R32 => Inst :: ULoad32 {
657
663
rd : into_reg,
658
664
mem,
659
665
srcloc : None ,
660
666
} ,
661
- types:: B64 | types:: I64 => Inst :: ULoad64 {
667
+ types:: B64 | types:: I64 | types :: R64 => Inst :: ULoad64 {
662
668
rd : into_reg,
663
669
mem,
664
670
srcloc : None ,
@@ -689,12 +695,12 @@ fn store_stack(mem: MemArg, from_reg: Reg, ty: Type) -> Inst {
689
695
mem,
690
696
srcloc : None ,
691
697
} ,
692
- types:: B32 | types:: I32 => Inst :: Store32 {
698
+ types:: B32 | types:: I32 | types :: R32 => Inst :: Store32 {
693
699
rd : from_reg,
694
700
mem,
695
701
srcloc : None ,
696
702
} ,
697
- types:: B64 | types:: I64 => Inst :: Store64 {
703
+ types:: B64 | types:: I64 | types :: R64 => Inst :: Store64 {
698
704
rd : from_reg,
699
705
mem,
700
706
srcloc : None ,
@@ -810,6 +816,35 @@ fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
810
816
caller_saved
811
817
}
812
818
819
+ fn gen_sp_adjust_insts < F : FnMut ( Inst ) > ( adj : u64 , is_sub : bool , mut f : F ) {
820
+ let alu_op = if is_sub { ALUOp :: Sub64 } else { ALUOp :: Add64 } ;
821
+
822
+ if let Some ( imm12) = Imm12 :: maybe_from_u64 ( adj) {
823
+ let adj_inst = Inst :: AluRRImm12 {
824
+ alu_op,
825
+ rd : writable_stack_reg ( ) ,
826
+ rn : stack_reg ( ) ,
827
+ imm12,
828
+ } ;
829
+ f ( adj_inst) ;
830
+ } else {
831
+ let tmp = writable_spilltmp_reg ( ) ;
832
+ let const_inst = Inst :: LoadConst64 {
833
+ rd : tmp,
834
+ const_data : adj,
835
+ } ;
836
+ let adj_inst = Inst :: AluRRRExtend {
837
+ alu_op,
838
+ rd : writable_stack_reg ( ) ,
839
+ rn : stack_reg ( ) ,
840
+ rm : tmp. to_reg ( ) ,
841
+ extendop : ExtendOp :: UXTX ,
842
+ } ;
843
+ f ( const_inst) ;
844
+ f ( adj_inst) ;
845
+ }
846
+ }
847
+
813
848
impl ABIBody for AArch64ABIBody {
814
849
type I = Inst ;
815
850
@@ -1024,6 +1059,29 @@ impl ABIBody for AArch64ABIBody {
1024
1059
store_stack ( MemArg :: NominalSPOffset ( sp_off, ty) , from_reg, ty)
1025
1060
}
1026
1061
1062
+ fn spillslots_to_stackmap ( & self , slots : & [ SpillSlot ] , state : & EmitState ) -> Stackmap {
1063
+ assert ! ( state. virtual_sp_offset >= 0 ) ;
1064
+ trace ! (
1065
+ "spillslots_to_stackmap: slots = {:?}, state = {:?}" ,
1066
+ slots,
1067
+ state
1068
+ ) ;
1069
+ let map_size = ( state. virtual_sp_offset + state. nominal_sp_to_fp ) as u32 ;
1070
+ let map_words = ( map_size + 7 ) / 8 ;
1071
+ let mut bits = std:: iter:: repeat ( false )
1072
+ . take ( map_words as usize )
1073
+ . collect :: < Vec < bool > > ( ) ;
1074
+
1075
+ let first_spillslot_word =
1076
+ ( ( self . stackslots_size + state. virtual_sp_offset as u32 ) / 8 ) as usize ;
1077
+ for & slot in slots {
1078
+ let slot = slot. get ( ) as usize ;
1079
+ bits[ first_spillslot_word + slot] = true ;
1080
+ }
1081
+
1082
+ Stackmap :: from_slice ( & bits[ ..] )
1083
+ }
1084
+
1027
1085
fn gen_prologue ( & mut self ) -> Vec < Inst > {
1028
1086
let mut insts = vec ! [ ] ;
1029
1087
if !self . call_conv . extends_baldrdash ( ) {
@@ -1059,6 +1117,9 @@ impl ABIBody for AArch64ABIBody {
1059
1117
}
1060
1118
let total_stacksize = ( total_stacksize + 15 ) & !15 ; // 16-align the stack.
1061
1119
1120
+ let mut total_sp_adjust = 0 ;
1121
+ let mut nominal_sp_to_real_sp = 0 ;
1122
+
1062
1123
if !self . call_conv . extends_baldrdash ( ) {
1063
1124
// Leaf functions with zero stack don't need a stack check if one's
1064
1125
// specified, otherwise always insert the stack check.
@@ -1069,42 +1130,29 @@ impl ABIBody for AArch64ABIBody {
1069
1130
}
1070
1131
}
1071
1132
if total_stacksize > 0 {
1072
- // sub sp, sp, #total_stacksize
1073
- if let Some ( imm12) = Imm12 :: maybe_from_u64 ( total_stacksize as u64 ) {
1074
- let sub_inst = Inst :: AluRRImm12 {
1075
- alu_op : ALUOp :: Sub64 ,
1076
- rd : writable_stack_reg ( ) ,
1077
- rn : stack_reg ( ) ,
1078
- imm12,
1079
- } ;
1080
- insts. push ( sub_inst) ;
1081
- } else {
1082
- let tmp = writable_spilltmp_reg ( ) ;
1083
- let const_inst = Inst :: LoadConst64 {
1084
- rd : tmp,
1085
- const_data : total_stacksize as u64 ,
1086
- } ;
1087
- let sub_inst = Inst :: AluRRRExtend {
1088
- alu_op : ALUOp :: Sub64 ,
1089
- rd : writable_stack_reg ( ) ,
1090
- rn : stack_reg ( ) ,
1091
- rm : tmp. to_reg ( ) ,
1092
- extendop : ExtendOp :: UXTX ,
1093
- } ;
1094
- insts. push ( const_inst) ;
1095
- insts. push ( sub_inst) ;
1096
- }
1133
+ total_sp_adjust += total_stacksize as u64 ;
1097
1134
}
1098
1135
}
1099
1136
1100
- // N.B.: "nominal SP", which we use to refer to stackslots
1101
- // and spillslots, is *here* (the value of SP at this program point).
1137
+ // N.B.: "nominal SP", which we use to refer to stackslots and
1138
+ // spillslots, is defined to be equal to the stack pointer at this point
1139
+ // in the prologue.
1140
+ //
1102
1141
// If we push any clobbers below, we emit a virtual-SP adjustment
1103
1142
// meta-instruction so that the nominal-SP references behave as if SP
1104
1143
// were still at this point. See documentation for
1105
1144
// [crate::isa::aarch64::abi](this module) for more details on
1106
1145
// stackframe layout and nominal-SP maintenance.
1107
1146
1147
+ if total_sp_adjust > 0 {
1148
+ // sub sp, sp, #total_stacksize
1149
+ gen_sp_adjust_insts (
1150
+ total_sp_adjust,
1151
+ /* is_sub = */ true ,
1152
+ |inst| insts. push ( inst) ,
1153
+ ) ;
1154
+ }
1155
+
1108
1156
// Save clobbered registers.
1109
1157
let ( clobbered_int, clobbered_vec) =
1110
1158
get_callee_saves ( self . call_conv , self . clobbered . to_vec ( ) ) ;
@@ -1148,10 +1196,11 @@ impl ABIBody for AArch64ABIBody {
1148
1196
srcloc : None ,
1149
1197
} ) ;
1150
1198
}
1199
+ nominal_sp_to_real_sp += clobber_size as i64 ;
1151
1200
1152
1201
if clobber_size > 0 {
1153
1202
insts. push ( Inst :: VirtualSPOffsetAdj {
1154
- offset : clobber_size as i64 ,
1203
+ offset : nominal_sp_to_real_sp ,
1155
1204
} ) ;
1156
1205
}
1157
1206
@@ -1245,6 +1294,10 @@ impl ABIBody for AArch64ABIBody {
1245
1294
. expect ( "frame size not computed before prologue generation" )
1246
1295
}
1247
1296
1297
+ fn stack_args_size ( & self ) -> u32 {
1298
+ self . sig . stack_arg_space as u32
1299
+ }
1300
+
1248
1301
fn get_spillslot_size ( & self , rc : RegClass , ty : Type ) -> u32 {
1249
1302
// We allocate in terms of 8-byte slots.
1250
1303
match ( rc, ty) {
@@ -1255,15 +1308,42 @@ impl ABIBody for AArch64ABIBody {
1255
1308
}
1256
1309
}
1257
1310
1258
- fn gen_spill ( & self , to_slot : SpillSlot , from_reg : RealReg , ty : Type ) -> Inst {
1311
+ fn gen_spill ( & self , to_slot : SpillSlot , from_reg : RealReg , ty : Option < Type > ) -> Inst {
1312
+ let ty = ty_from_ty_hint_or_reg_class ( from_reg. to_reg ( ) , ty) ;
1259
1313
self . store_spillslot ( to_slot, ty, from_reg. to_reg ( ) )
1260
1314
}
1261
1315
1262
- fn gen_reload ( & self , to_reg : Writable < RealReg > , from_slot : SpillSlot , ty : Type ) -> Inst {
1316
+ fn gen_reload (
1317
+ & self ,
1318
+ to_reg : Writable < RealReg > ,
1319
+ from_slot : SpillSlot ,
1320
+ ty : Option < Type > ,
1321
+ ) -> Inst {
1322
+ let ty = ty_from_ty_hint_or_reg_class ( to_reg. to_reg ( ) . to_reg ( ) , ty) ;
1263
1323
self . load_spillslot ( from_slot, ty, to_reg. map ( |r| r. to_reg ( ) ) )
1264
1324
}
1265
1325
}
1266
1326
1327
+ /// Return a type either from an optional type hint, or if not, from the default
1328
+ /// type associated with the given register's class. This is used to generate
1329
+ /// loads/spills appropriately given the type of value loaded/stored (which may
1330
+ /// be narrower than the spillslot). We usually have the type because the
1331
+ /// regalloc usually provides the vreg being spilled/reloaded, and we know every
1332
+ /// vreg's type. However, the regalloc *can* request a spill/reload without an
1333
+ /// associated vreg when needed to satisfy a safepoint (which requires all
1334
+ /// ref-typed values, even those in real registers in the original vcode, to be
1335
+ /// in spillslots).
1336
+ fn ty_from_ty_hint_or_reg_class ( r : Reg , ty : Option < Type > ) -> Type {
1337
+ match ( ty, r. get_class ( ) ) {
1338
+ // If the type is provided
1339
+ ( Some ( t) , _) => t,
1340
+ // If no type is provided, this should be a register spill for a
1341
+ // safepoint, so we only expect I64 (integer) registers.
1342
+ ( None , RegClass :: I64 ) => I64 ,
1343
+ _ => panic ! ( "Unexpected register class!" ) ,
1344
+ }
1345
+ }
1346
+
1267
1347
enum CallDest {
1268
1348
ExtName ( ir:: ExternalName , RelocDistance ) ,
1269
1349
Reg ( Reg ) ,
@@ -1342,7 +1422,7 @@ impl AArch64ABICall {
1342
1422
}
1343
1423
}
1344
1424
1345
- fn adjust_stack < C : LowerCtx < I = Inst > > ( ctx : & mut C , amount : u64 , is_sub : bool ) {
1425
+ fn adjust_stack_and_nominal_sp < C : LowerCtx < I = Inst > > ( ctx : & mut C , amount : u64 , is_sub : bool ) {
1346
1426
if amount == 0 {
1347
1427
return ;
1348
1428
}
@@ -1356,27 +1436,9 @@ fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
1356
1436
offset : sp_adjustment,
1357
1437
} ) ;
1358
1438
1359
- let alu_op = if is_sub { ALUOp :: Sub64 } else { ALUOp :: Add64 } ;
1360
- if let Some ( imm12) = Imm12 :: maybe_from_u64 ( amount) {
1361
- ctx. emit ( Inst :: AluRRImm12 {
1362
- alu_op,
1363
- rd : writable_stack_reg ( ) ,
1364
- rn : stack_reg ( ) ,
1365
- imm12,
1366
- } )
1367
- } else {
1368
- ctx. emit ( Inst :: LoadConst64 {
1369
- rd : writable_spilltmp_reg ( ) ,
1370
- const_data : amount,
1371
- } ) ;
1372
- ctx. emit ( Inst :: AluRRRExtend {
1373
- alu_op,
1374
- rd : writable_stack_reg ( ) ,
1375
- rn : stack_reg ( ) ,
1376
- rm : spilltmp_reg ( ) ,
1377
- extendop : ExtendOp :: UXTX ,
1378
- } ) ;
1379
- }
1439
+ gen_sp_adjust_insts ( amount, is_sub, |inst| {
1440
+ ctx. emit ( inst) ;
1441
+ } ) ;
1380
1442
}
1381
1443
1382
1444
impl ABICall for AArch64ABICall {
@@ -1392,12 +1454,12 @@ impl ABICall for AArch64ABICall {
1392
1454
1393
1455
fn emit_stack_pre_adjust < C : LowerCtx < I = Self :: I > > ( & self , ctx : & mut C ) {
1394
1456
let off = self . sig . stack_arg_space + self . sig . stack_ret_space ;
1395
- adjust_stack ( ctx, off as u64 , /* is_sub = */ true )
1457
+ adjust_stack_and_nominal_sp ( ctx, off as u64 , /* is_sub = */ true )
1396
1458
}
1397
1459
1398
1460
fn emit_stack_post_adjust < C : LowerCtx < I = Self :: I > > ( & self , ctx : & mut C ) {
1399
1461
let off = self . sig . stack_arg_space + self . sig . stack_ret_space ;
1400
- adjust_stack ( ctx, off as u64 , /* is_sub = */ false )
1462
+ adjust_stack_and_nominal_sp ( ctx, off as u64 , /* is_sub = */ false )
1401
1463
}
1402
1464
1403
1465
fn emit_copy_reg_to_arg < C : LowerCtx < I = Self :: I > > (
@@ -1452,7 +1514,7 @@ impl ABICall for AArch64ABICall {
1452
1514
self . emit_copy_reg_to_arg ( ctx, i, rd. to_reg ( ) ) ;
1453
1515
}
1454
1516
match & self . dest {
1455
- & CallDest :: ExtName ( ref name, RelocDistance :: Near ) => ctx. emit ( Inst :: Call {
1517
+ & CallDest :: ExtName ( ref name, RelocDistance :: Near ) => ctx. emit_safepoint ( Inst :: Call {
1456
1518
info : Box :: new ( CallInfo {
1457
1519
dest : name. clone ( ) ,
1458
1520
uses,
@@ -1468,7 +1530,7 @@ impl ABICall for AArch64ABICall {
1468
1530
offset : 0 ,
1469
1531
srcloc : self . loc ,
1470
1532
} ) ;
1471
- ctx. emit ( Inst :: CallInd {
1533
+ ctx. emit_safepoint ( Inst :: CallInd {
1472
1534
info : Box :: new ( CallIndInfo {
1473
1535
rn : spilltmp_reg ( ) ,
1474
1536
uses,
@@ -1478,7 +1540,7 @@ impl ABICall for AArch64ABICall {
1478
1540
} ) ,
1479
1541
} ) ;
1480
1542
}
1481
- & CallDest :: Reg ( reg) => ctx. emit ( Inst :: CallInd {
1543
+ & CallDest :: Reg ( reg) => ctx. emit_safepoint ( Inst :: CallInd {
1482
1544
info : Box :: new ( CallIndInfo {
1483
1545
rn : reg,
1484
1546
uses,
0 commit comments