Help handling opaque AArch64 immediates

Hello LLVM,
I'm playing with a new ISD::OPAQUE instruction to make hoisting first
class and eliminate a lot of tweaky flag setting/checking around
opaque constants. It's going well for the IR and x86, but I now I
need to sort out details for all the other targets.

To start, can someone please advise on the AAarch64 equivalent of
these X86 patterns?

// Opaque values become mov immediate to register
def : Pat<(i64 (opaque imm:$src)), (MOV64ri imm:$src)>;
def : Pat<(i32 (opaque imm:$src)), (MOV32ri imm:$src)>;
def : Pat<(i16 (opaque imm:$src)), (MOV16ri imm:$src)>;

The 'opaque' here is of course hiding the immediate from folding.
What I'm looking for is the AAarch64 equivalent to copying the opaque
immediate into a register.

I promise your help won't be construed as an endorsement of the
ISD::OPAQUE idea :slight_smile:

Thanks,
-steve

// Opaque values become mov immediate to register
def : Pat<(i64 (opaque imm:$src)), (MOV64ri imm:$src)>;
def : Pat<(i32 (opaque imm:$src)), (MOV32ri imm:$src)>;

These two are pseudo-instructions: MOVi32imm and MOVi64imm work in
basically the same way as the x86 instructions I think. They get
expanded into real sequences in AArch64ExpandPseudos.cpp.

def : Pat<(i16 (opaque imm:$src)), (MOV16ri imm:$src)>;

This is slightly different, the instruction is most likely MOVZWi,
with two wrinkles: i16 isn't a legal type and there's an extra "shift
amount" immediate. For your purposes something along the lines of

  def : Pat<(i64 (opaque (imm0_65535 imm:$src))), (MOVZWi imm:$src, 0)>;

is probably right (untested, I'm afraid, but it should contain the
essential ingredients).

Cheers.

Tim.

  def : Pat<(i64 (opaque (imm0_65535 imm:$src))), (MOVZWi imm:$src, 0)>;

Sorry, that should certainly not be i64 in there.

   def : Pat<(i32 (opaque (imm0_65535 imm:$src))), (MOVZWi imm:$src, 0)>;

is more likely.

Tim.

Thanks Tim! Ignoring the 16-bit issue for the moment, I have your
suggested pattern:

def : Pat<(i64 (opaque imm:$src)), (MOVi64imm imm:$src)>;
def : Pat<(i32 (opaque imm:$src)), (MOVi32imm imm:$src)>;

LLVM is happy, except a test fails with what I think is a 64-bit vs.
32-bit discrepancy.

test/CodeGen/AArch64/arm64-const-addr.ll:8:10: error: expected string
not found in input
; CHECK: movz w8, #0x40f, lsl #16
         ^
<stdin>:3:2: note: scanning from here
.align 2
^
<stdin>:6:2: note: possible intended match here
movz x8, #0x40f, lsl #16

The test wants a movz w8 (32-bit), but opaque lowering produced a movz
x8 (64-bit). Since the constant pointer is born as "inttoptr i64", is
movz x8 be correct and the test wrong?

Thanks,
-steve

Full test file:

; RUN: llc -mtriple=arm64-darwin-unknown < %s | FileCheck %s
%T = type { i32, i32, i32, i32 }
; Test if the constant base address gets only materialized once.
define i32 @test1() nounwind {
; CHECK-LABEL: test1
; CHECK: movz w8, #0x40f, lsl #16
; CHECK-NEXT: movk w8, #0xc000
; CHECK-NEXT: ldp w9, w10, [x8, #4]
; CHECK: ldr w8, [x8, #12]
  %at = inttoptr i64 68141056 to %T*
  %o1 = getelementptr %T, %T* %at, i32 0, i32 1
  %t1 = load i32, i32* %o1
  %o2 = getelementptr %T, %T* %at, i32 0, i32 2
  %t2 = load i32, i32* %o2
  %a1 = add i32 %t1, %t2
  %o3 = getelementptr %T, %T* %at, i32 0, i32 3
  %t3 = load i32, i32* %o3
  %a2 = add i32 %a1, %t3
  ret i32 %a2
}

Hi Steve,

The test wants a movz w8 (32-bit), but opaque lowering produced a movz
x8 (64-bit). Since the constant pointer is born as "inttoptr i64", is
movz x8 be correct and the test wrong?

They're semantically equivalent. "movz w8, ..." will zero out the high
bits of x8 that don't overlap with w8.

There's a very mild preference for the w8 form in case a CPU ever
decides to do some low-power tricks when only a 32-bits operation is
needed. I'm not actually aware of it ever making a difference though.

Cheers.

Tim.