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 > > ,
@@ -811,6 +815,35 @@ fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
811
815
caller_saved
812
816
}
813
817
818
+ fn gen_sp_adjust_insts < F : FnMut ( Inst ) > ( adj : u64 , is_sub : bool , mut f : F ) {
819
+ let alu_op = if is_sub { ALUOp :: Sub64 } else { ALUOp :: Add64 } ;
820
+
821
+ if let Some ( imm12) = Imm12 :: maybe_from_u64 ( adj) {
822
+ let adj_inst = Inst :: AluRRImm12 {
823
+ alu_op,
824
+ rd : writable_stack_reg ( ) ,
825
+ rn : stack_reg ( ) ,
826
+ imm12,
827
+ } ;
828
+ f ( adj_inst) ;
829
+ } else {
830
+ let tmp = writable_spilltmp_reg ( ) ;
831
+ let const_inst = Inst :: LoadConst64 {
832
+ rd : tmp,
833
+ const_data : adj,
834
+ } ;
835
+ let adj_inst = Inst :: AluRRRExtend {
836
+ alu_op,
837
+ rd : writable_stack_reg ( ) ,
838
+ rn : stack_reg ( ) ,
839
+ rm : tmp. to_reg ( ) ,
840
+ extendop : ExtendOp :: UXTX ,
841
+ } ;
842
+ f ( const_inst) ;
843
+ f ( adj_inst) ;
844
+ }
845
+ }
846
+
814
847
impl ABIBody for AArch64ABIBody {
815
848
type I = Inst ;
816
849
@@ -1025,6 +1058,29 @@ impl ABIBody for AArch64ABIBody {
1025
1058
store_stack ( MemArg :: NominalSPOffset ( sp_off, ty) , from_reg, ty)
1026
1059
}
1027
1060
1061
+ fn spillslots_to_stackmap ( & self , slots : & [ SpillSlot ] , state : & EmitState ) -> Stackmap {
1062
+ assert ! ( state. virtual_sp_offset >= 0 ) ;
1063
+ trace ! (
1064
+ "spillslots_to_stackmap: slots = {:?}, state = {:?}" ,
1065
+ slots,
1066
+ state
1067
+ ) ;
1068
+ let map_size = ( state. virtual_sp_offset + state. nominal_sp_to_fp ) as u32 ;
1069
+ let map_words = ( map_size + 7 ) / 8 ;
1070
+ let mut bits = std:: iter:: repeat ( false )
1071
+ . take ( map_words as usize )
1072
+ . collect :: < Vec < bool > > ( ) ;
1073
+
1074
+ let first_spillslot_word =
1075
+ ( ( self . stackslots_size + state. virtual_sp_offset as u32 ) / 8 ) as usize ;
1076
+ for & slot in slots {
1077
+ let slot = slot. get ( ) as usize ;
1078
+ bits[ first_spillslot_word + slot] = true ;
1079
+ }
1080
+
1081
+ Stackmap :: from_slice ( & bits[ ..] )
1082
+ }
1083
+
1028
1084
fn gen_prologue ( & mut self ) -> Vec < Inst > {
1029
1085
let mut insts = vec ! [ ] ;
1030
1086
if !self . call_conv . extends_baldrdash ( ) {
@@ -1060,6 +1116,9 @@ impl ABIBody for AArch64ABIBody {
1060
1116
}
1061
1117
let total_stacksize = ( total_stacksize + 15 ) & !15 ; // 16-align the stack.
1062
1118
1119
+ let mut total_sp_adjust = 0 ;
1120
+ let mut nominal_sp_to_real_sp = 0 ;
1121
+
1063
1122
if !self . call_conv . extends_baldrdash ( ) {
1064
1123
// Leaf functions with zero stack don't need a stack check if one's
1065
1124
// specified, otherwise always insert the stack check.
@@ -1070,42 +1129,28 @@ impl ABIBody for AArch64ABIBody {
1070
1129
}
1071
1130
}
1072
1131
if total_stacksize > 0 {
1073
- // sub sp, sp, #total_stacksize
1074
- if let Some ( imm12) = Imm12 :: maybe_from_u64 ( total_stacksize as u64 ) {
1075
- let sub_inst = Inst :: AluRRImm12 {
1076
- alu_op : ALUOp :: Sub64 ,
1077
- rd : writable_stack_reg ( ) ,
1078
- rn : stack_reg ( ) ,
1079
- imm12,
1080
- } ;
1081
- insts. push ( sub_inst) ;
1082
- } else {
1083
- let tmp = writable_spilltmp_reg ( ) ;
1084
- let const_inst = Inst :: LoadConst64 {
1085
- rd : tmp,
1086
- const_data : total_stacksize as u64 ,
1087
- } ;
1088
- let sub_inst = Inst :: AluRRRExtend {
1089
- alu_op : ALUOp :: Sub64 ,
1090
- rd : writable_stack_reg ( ) ,
1091
- rn : stack_reg ( ) ,
1092
- rm : tmp. to_reg ( ) ,
1093
- extendop : ExtendOp :: UXTX ,
1094
- } ;
1095
- insts. push ( const_inst) ;
1096
- insts. push ( sub_inst) ;
1097
- }
1132
+ total_sp_adjust += total_stacksize as u64 ;
1098
1133
}
1099
1134
}
1100
1135
1101
- // N.B.: "nominal SP", which we use to refer to stackslots
1102
- // and spillslots, is *here* (the value of SP at this program point).
1136
+ // N.B.: "nominal SP", which we use to refer to stackslots and
1137
+ // spillslots, is right here.
1138
+ //
1103
1139
// If we push any clobbers below, we emit a virtual-SP adjustment
1104
1140
// meta-instruction so that the nominal-SP references behave as if SP
1105
1141
// were still at this point. See documentation for
1106
1142
// [crate::isa::aarch64::abi](this module) for more details on
1107
1143
// stackframe layout and nominal-SP maintenance.
1108
1144
1145
+ if total_sp_adjust > 0 {
1146
+ // sub sp, sp, #total_stacksize
1147
+ gen_sp_adjust_insts (
1148
+ total_sp_adjust,
1149
+ /* is_sub = */ true ,
1150
+ |inst| insts. push ( inst) ,
1151
+ ) ;
1152
+ }
1153
+
1109
1154
// Save clobbered registers.
1110
1155
let ( clobbered_int, clobbered_vec) =
1111
1156
get_callee_saves ( self . call_conv , self . clobbered . to_vec ( ) ) ;
@@ -1149,10 +1194,11 @@ impl ABIBody for AArch64ABIBody {
1149
1194
srcloc : None ,
1150
1195
} ) ;
1151
1196
}
1197
+ nominal_sp_to_real_sp += clobber_size as i64 ;
1152
1198
1153
1199
if clobber_size > 0 {
1154
1200
insts. push ( Inst :: VirtualSPOffsetAdj {
1155
- offset : clobber_size as i64 ,
1201
+ offset : nominal_sp_to_real_sp ,
1156
1202
} ) ;
1157
1203
}
1158
1204
@@ -1246,6 +1292,10 @@ impl ABIBody for AArch64ABIBody {
1246
1292
. expect ( "frame size not computed before prologue generation" )
1247
1293
}
1248
1294
1295
+ fn stack_args_size ( & self ) -> u32 {
1296
+ self . sig . stack_arg_space as u32
1297
+ }
1298
+
1249
1299
fn get_spillslot_size ( & self , rc : RegClass , ty : Type ) -> u32 {
1250
1300
// We allocate in terms of 8-byte slots.
1251
1301
match ( rc, ty) {
@@ -1256,15 +1306,30 @@ impl ABIBody for AArch64ABIBody {
1256
1306
}
1257
1307
}
1258
1308
1259
- fn gen_spill ( & self , to_slot : SpillSlot , from_reg : RealReg , ty : Type ) -> Inst {
1309
+ fn gen_spill ( & self , to_slot : SpillSlot , from_reg : RealReg , ty : Option < Type > ) -> Inst {
1310
+ let ty = ty_from_ty_hint_or_reg_class ( from_reg. to_reg ( ) , ty) ;
1260
1311
self . store_spillslot ( to_slot, ty, from_reg. to_reg ( ) )
1261
1312
}
1262
1313
1263
- fn gen_reload ( & self , to_reg : Writable < RealReg > , from_slot : SpillSlot , ty : Type ) -> Inst {
1314
+ fn gen_reload (
1315
+ & self ,
1316
+ to_reg : Writable < RealReg > ,
1317
+ from_slot : SpillSlot ,
1318
+ ty : Option < Type > ,
1319
+ ) -> Inst {
1320
+ let ty = ty_from_ty_hint_or_reg_class ( to_reg. to_reg ( ) . to_reg ( ) , ty) ;
1264
1321
self . load_spillslot ( from_slot, ty, to_reg. map ( |r| r. to_reg ( ) ) )
1265
1322
}
1266
1323
}
1267
1324
1325
+ fn ty_from_ty_hint_or_reg_class ( r : Reg , ty : Option < Type > ) -> Type {
1326
+ match ( ty, r. get_class ( ) ) {
1327
+ ( Some ( t) , _) => t,
1328
+ ( None , RegClass :: I64 ) => I64 ,
1329
+ _ => panic ! ( "Unexpected register class!" ) ,
1330
+ }
1331
+ }
1332
+
1268
1333
enum CallDest {
1269
1334
ExtName ( ir:: ExternalName , RelocDistance ) ,
1270
1335
Reg ( Reg ) ,
@@ -1343,7 +1408,7 @@ impl AArch64ABICall {
1343
1408
}
1344
1409
}
1345
1410
1346
- fn adjust_stack < C : LowerCtx < I = Inst > > ( ctx : & mut C , amount : u64 , is_sub : bool ) {
1411
+ fn adjust_stack_and_nominal_sp < C : LowerCtx < I = Inst > > ( ctx : & mut C , amount : u64 , is_sub : bool ) {
1347
1412
if amount == 0 {
1348
1413
return ;
1349
1414
}
@@ -1357,27 +1422,9 @@ fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
1357
1422
offset : sp_adjustment,
1358
1423
} ) ;
1359
1424
1360
- let alu_op = if is_sub { ALUOp :: Sub64 } else { ALUOp :: Add64 } ;
1361
- if let Some ( imm12) = Imm12 :: maybe_from_u64 ( amount) {
1362
- ctx. emit ( Inst :: AluRRImm12 {
1363
- alu_op,
1364
- rd : writable_stack_reg ( ) ,
1365
- rn : stack_reg ( ) ,
1366
- imm12,
1367
- } )
1368
- } else {
1369
- ctx. emit ( Inst :: LoadConst64 {
1370
- rd : writable_spilltmp_reg ( ) ,
1371
- const_data : amount,
1372
- } ) ;
1373
- ctx. emit ( Inst :: AluRRRExtend {
1374
- alu_op,
1375
- rd : writable_stack_reg ( ) ,
1376
- rn : stack_reg ( ) ,
1377
- rm : spilltmp_reg ( ) ,
1378
- extendop : ExtendOp :: UXTX ,
1379
- } ) ;
1380
- }
1425
+ gen_sp_adjust_insts ( amount, is_sub, |inst| {
1426
+ ctx. emit ( inst) ;
1427
+ } ) ;
1381
1428
}
1382
1429
1383
1430
impl ABICall for AArch64ABICall {
@@ -1393,12 +1440,12 @@ impl ABICall for AArch64ABICall {
1393
1440
1394
1441
fn emit_stack_pre_adjust < C : LowerCtx < I = Self :: I > > ( & self , ctx : & mut C ) {
1395
1442
let off = self . sig . stack_arg_space + self . sig . stack_ret_space ;
1396
- adjust_stack ( ctx, off as u64 , /* is_sub = */ true )
1443
+ adjust_stack_and_nominal_sp ( ctx, off as u64 , /* is_sub = */ true )
1397
1444
}
1398
1445
1399
1446
fn emit_stack_post_adjust < C : LowerCtx < I = Self :: I > > ( & self , ctx : & mut C ) {
1400
1447
let off = self . sig . stack_arg_space + self . sig . stack_ret_space ;
1401
- adjust_stack ( ctx, off as u64 , /* is_sub = */ false )
1448
+ adjust_stack_and_nominal_sp ( ctx, off as u64 , /* is_sub = */ false )
1402
1449
}
1403
1450
1404
1451
fn emit_copy_reg_to_arg < C : LowerCtx < I = Self :: I > > (
@@ -1453,7 +1500,7 @@ impl ABICall for AArch64ABICall {
1453
1500
self . emit_copy_reg_to_arg ( ctx, i, rd. to_reg ( ) ) ;
1454
1501
}
1455
1502
match & self . dest {
1456
- & CallDest :: ExtName ( ref name, RelocDistance :: Near ) => ctx. emit ( Inst :: Call {
1503
+ & CallDest :: ExtName ( ref name, RelocDistance :: Near ) => ctx. emit_safepoint ( Inst :: Call {
1457
1504
info : Box :: new ( CallInfo {
1458
1505
dest : name. clone ( ) ,
1459
1506
uses,
@@ -1469,7 +1516,7 @@ impl ABICall for AArch64ABICall {
1469
1516
offset : 0 ,
1470
1517
srcloc : self . loc ,
1471
1518
} ) ;
1472
- ctx. emit ( Inst :: CallInd {
1519
+ ctx. emit_safepoint ( Inst :: CallInd {
1473
1520
info : Box :: new ( CallIndInfo {
1474
1521
rn : spilltmp_reg ( ) ,
1475
1522
uses,
@@ -1479,7 +1526,7 @@ impl ABICall for AArch64ABICall {
1479
1526
} ) ,
1480
1527
} ) ;
1481
1528
}
1482
- & CallDest :: Reg ( reg) => ctx. emit ( Inst :: CallInd {
1529
+ & CallDest :: Reg ( reg) => ctx. emit_safepoint ( Inst :: CallInd {
1483
1530
info : Box :: new ( CallIndInfo {
1484
1531
rn : reg,
1485
1532
uses,
0 commit comments