Expands `li rd, imm` into the minimal real instruction sequence.
(rd: Reg, imm: i64)
| 391 | if (-2048..=2047).contains(&imm) { |
| 392 | return vec![RealInstruction::Addi(Addi::new(rd, 0, imm as i32))]; |
| 393 | } |
| 394 | |
| 395 | // 32-bit case: the value must fit into a signed 32-bit word. |
| 396 | // If so, we can use lui + addi. |
| 397 | let fits_i32 = (-2_147_483_648..=2_147_483_647).contains(&imm); |
| 398 | if fits_i32 { |
| 399 | let imm32 = imm as i32; |
| 400 | let lo12 = imm32 & 0xFFF; // unsigned lower 12 bits |
| 401 | let lo12_signed = if lo12 >= 0x800 { lo12 - 0x1000 } else { lo12 }; |
| 402 | // hi20_val is the value whose upper 20 bits are the lui immediate. |
| 403 | let hi20_val = imm32.wrapping_sub(lo12_signed); // hi20_val is a multiple of 0x1000 |
| 404 | let mut out = vec![RealInstruction::Lui(Lui::new(rd, hi20_val))]; |
| 405 | if lo12_signed != 0 { |
| 406 | out.push(RealInstruction::Addi(Addi::new(rd, rd, lo12_signed))); |
| 407 | } |
| 408 | return out; |
| 409 | } |
| 410 | |
| 411 | // 64-bit case: construct the value from two 32-bit halves. |
| 412 | // The expansion uses t1 (x6) as a temporary - it is caller-saved |
| 413 | let low32 = (imm & 0xFFFF_FFFF) as i32; // as 32-bit signed |
| 414 | let high32 = ((imm >> 32) & 0xFFFF_FFFF) as i32; // as 32-bit signed |
| 415 | |
| 416 | // Helper to produce the lui+addi sequence for a 32-bit constant |
| 417 | fn load32(rd: Reg, val32: i32) -> Vec<RealInstruction> { |
| 418 | let lo12 = val32 & 0xFFF; |
| 419 | let lo12_signed = if lo12 >= 0x800 { lo12 - 0x1000 } else { lo12 }; |
| 420 | let hi20 = val32.wrapping_sub(lo12_signed); |
| 421 | let mut seq = vec![RealInstruction::Lui(Lui::new(rd, hi20))]; |
| 422 | if lo12_signed != 0 { |
| 423 | seq.push(RealInstruction::Addi(Addi::new(rd, rd, lo12_signed))); |
| 424 | } |
| 425 | seq |
| 426 | } |
| 427 | |
| 428 | let mut seq = Vec::new(); |
| 429 | |
| 430 | // Load the high 32 bits into t1 and shift them left by 32. |
| 431 | seq.append(&mut load32(T1, high32)); // T1 = sign_ext(high32) |
| 432 | seq.push(RealInstruction::Slli(Slli::new(T1, T1, 32))); // T1 = high32 << 32 |
| 433 | |
| 434 | // Load the low 32 bits into rd, then zero-extend to 64 bits. |
| 435 | seq.append(&mut load32(rd, low32)); // rd = sign_ext(low32) |
| 436 | seq.push(RealInstruction::Slli(Slli::new(rd, rd, 32))); // clear upper 32 bits |
| 437 | seq.push(RealInstruction::Srli(Srli::new(rd, rd, 32))); // rd = zero_ext(low32) |
| 438 | |
| 439 | // Combine: rd = zero_ext(low32) | (high32 << 32) |
| 440 | seq.push(RealInstruction::Or(Or::new(rd, rd, T1))); |
| 441 | |
| 442 | seq |
| 443 | } |