On Fri, 20 Feb 2026 17:07:13 GMT, Andrew Haley <[email protected]> wrote:
>> That would be really useful! I tinkered with it a bit but would be nice to
>> see what you had in mind
>
>> That would be really useful! I tinkered with it a bit but would be nice to
>> see what you had in mind
>
> Like this:
>
> address generate_intpoly_montgomeryMult_P256() {
>
> __ align(CodeEntryAlignment);
> StubId stub_id = StubId::stubgen_intpoly_montgomeryMult_P256_id;
> StubCodeMark mark(this, stub_id);
> address start = __ pc();
> __ enter();
>
> static const int64_t modulus[] = {
> 0x000fffffffffffffL, 0x00000fffffffffffL,
> 0x0000001000000000L, 0x0000ffffffff0000L,
> 0L
> };
>
> int shift1 = 12; // 64 - bits per limb
> int shift2 = 52; // bits per limb
>
> // Registers that are used throughout entire routine
> const Register a = c_rarg0;
> const Register b = c_rarg1;
> const Register result = c_rarg2;
>
> RegSet regs = RegSet::range(r0, r28) + rfp + lr - a - b - result;
> FloatRegSet floatRegs = FloatRegSet::range(v0, v31)
> - FloatRegSet::range(v8, v15) // Caller saved vectors
> - FloatRegSet::range(v16, v31); // Manually-allocated vectors
>
> auto common_regs = regs.begin();
> Register limb_mask = *common_regs++,
> c_ptr = *common_regs++,
> mod_0 = *common_regs++,
> mod_1 = *common_regs++,
> mod_3 = *common_regs++,
> mod_4 = *common_regs++,
> b_0 = *common_regs++,
> b_1 = *common_regs++,
> b_2 = *common_regs++,
> b_3 = *common_regs++,
> b_4 = *common_regs++;
> regs = common_regs.remaining();
>
> auto common_vectors = floatRegs.begin();
> FloatRegister limb_mask_vec = *common_vectors++,
> b_lows = *common_vectors++,
> b_highs = *common_vectors++,
> a_vals = *common_vectors++;
>
> // Push callee saved registers on to the stack
> RegSet callee_saved = RegSet::range(r19, r28);
> __ push(callee_saved, sp);
>
> // Allocate space on the stack for carry values
> __ sub(sp, sp, 48);
> __ mov(c_ptr, sp);
>
> // Calculate limb mask
> __ mov(limb_mask, -UCONST64(1) >> (64 - shift2));
> __ dup(limb_mask_vec, __ T2D, limb_mask);
>
> // Load input arrays and modulus
> {
> auto r = regs.begin();
> Register a_ptr = *r++, mod_ptr = *r++;
> __ add(a_ptr, a, 24);
> __ lea(mod_ptr, ExternalAddress((address)modulus));
> __ ldr(b_0, Address(b));
> __ ldr(b_1, Address(b, 8));
> __ ldr(b_2, Address(b, 16));
> __ ldr(b_3, Address(b, 24));
> __ ldr(b_4, Address(b, 32));
> __ ldr(mod_0, __ post(mod_ptr, 8));
> __ ldr(mod_1, __ post(mod_ptr, 8));
> __ ldr(mod_3, __ post(mod_ptr, 8));
> __ ldr(mod_4, mod_ptr)...
Note that in a few places I've had to push back dead registers so that they can
be reused. This is necessary because the live ranges for some registers
partailly overlap.
It's much better if you don't do that: instead, write a structured
assembly-language program in which registers are allocated in scopes as needed,
as I've done in the section which begins like this:
// Load input arrays and modulus
{
auto r = regs.begin();
Register a_ptr = *r++, mod_ptr = *r++;
here, the register that contain`a_ptr` and `mod_ptr` are taken from the outer
block, and are free for reuse when the inner block exits.
I hope the advantages of this style are clear: the program is easier to write,
to maintain, and much less risky. Also, and most importantly for me, it's much
easier to review!
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/27946#discussion_r2834240522