LDTNP

Load unprivileged pair of registers, with non-temporal hint

This instruction calculates an address from a base register value and an immediate offset, loads two 64-bit doublewords from memory, and writes them to two registers. For information about addressing modes, see Load/Store addressing modes. For information about non-temporal pair instructions, see Load/Store non-temporal pair.

Explicit Memory effects produced by the instruction behave as if the instruction was executed at EL0 if the Effective value of PSTATE.UAO is 0 and either:

  • The instruction is executed at EL1.
  • The instruction is executed at EL2 when the Effective value of HCR_EL2.{E2H, TGE} is {1, 1}.
  • Otherwise, the Explicit Memory effects operate with the restrictions determined by the Exception level at which the instruction is executed.

    Encoding: Signed offset

    Variants: FEAT_LSUI (ARMv9.6)

    313029282726252423222120191817161514131211109876543210
    1110100001
    opcVRLimm7Rt2RnRt

    LDTNP <Xt1>, <Xt2>, [<Xn|SP>{, #<imm>}]

    Decoding algorithm

    if !IsFeatureImplemented(FEAT_LSUI) then EndOfDecode(Decode_UNDEF);
    
    constant integer t = UInt(Rt);
    constant integer t2 = UInt(Rt2);
    constant integer n = UInt(Rn);
    constant boolean nontemporal = TRUE;
    constant integer datasize = 64;
    constant bits(64) offset = LSL(SignExtend(imm7, 64), 3);
    constant boolean tagchecked = n != 31;
    
    boolean rt_unknown = FALSE;
    if t == t2 then
        constant Constraint c = ConstrainUnpredictable(Unpredictable_LDPOVERLAP);
        assert c IN {Constraint_UNKNOWN, Constraint_UNDEF, Constraint_NOP};
        case c of
            when Constraint_UNKNOWN    rt_unknown = TRUE;    // Result is UNKNOWN
            when Constraint_UNDEF      EndOfDecode(Decode_UNDEF);
            when Constraint_NOP        EndOfDecode(Decode_NOP);

    Operation

    bits(64) address;
    bits(64) address2;
    constant integer dbytes = datasize DIV 8;
    bits(datasize) data1;
    bits(datasize) data2;
    constant boolean privileged = AArch64.IsUnprivAccessPriv();
    constant AccessDescriptor accdesc = CreateAccDescGPR(MemOp_LOAD, nontemporal, privileged,
                                                         tagchecked);
    
    if n == 31 then
        CheckSPAlignment();
        address = SP[64];
    else
        address = X[n, 64];
    
    address = AddressAdd(address, offset, accdesc);
    
    if accdesc.ispair then
        constant bits(2*datasize) full_data = Mem[address, 2 * dbytes, accdesc];
        if BigEndian(accdesc.acctype) then
            data2 = full_data<(datasize-1):0>;
            data1 = full_data<(2*datasize-1):datasize>;
        else
            data1 = full_data<(datasize-1):0>;
            data2 = full_data<(2*datasize-1):datasize>;
    else
        address2 = AddressIncrement(address, dbytes, accdesc);
        data1 = Mem[address , dbytes, accdesc];
        data2 = Mem[address2, dbytes, accdesc];
    if rt_unknown then
        data1 = bits(datasize) UNKNOWN;
        data2 = bits(datasize) UNKNOWN;
    X[t, datasize]  = data1;
    X[t2, datasize] = data2;

    Explanations

    <Xt1>: Is the 64-bit name of the first general-purpose register to be transferred, encoded in the "Rt" field.
    <Xt2>: Is the 64-bit name of the second general-purpose register to be transferred, encoded in the "Rt2" field.
    <Xn|SP>: Is the 64-bit name of the general-purpose base register or stack pointer, encoded in the "Rn" field.
    <imm>: Is the optional signed immediate byte offset, a multiple of 8 in the range -512 to 504, defaulting to 0 and encoded in the "imm7" field as <imm>/8.

    Operational Notes

    If PSTATE.DIT is 1, the timing of this instruction is insensitive to the value of the data being loaded or stored.