Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions llvm/lib/Target/AIE/AIEBaseRegisterBankInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,16 @@ bool AIEBaseRegisterBankInfo::requiresGPRRegBank(const MachineInstr &MI,
case TargetOpcode::G_SEXT:
case TargetOpcode::G_ZEXT:
return true;
case TargetOpcode::G_ICMP:
// Scalar and pointer comparisons are only supported on GPRs (see the
// G_ICMP handling in getInstrMapping).
return true;
case TargetOpcode::G_SELECT: {
// Pointers (and scalars) are selected on GPRs (see the G_SELECT handling
// in getInstrMapping). Vector selects stay on the vector bank.
LLT Ty = MRI.getType(MI.getOperand(0).getReg());
return Ty.isPointer() || (Ty.isScalar() && Ty.getSizeInBits() <= 32);
}
default:
break;
}
Expand All @@ -332,6 +342,24 @@ bool AIEBaseRegisterBankInfo::requiresGPRRegBank(const MachineInstr &MI,
});
}

bool AIEBaseRegisterBankInfo::pointerLoadDefShouldBeGPR(
const MachineInstr &MI, const MachineRegisterInfo &MRI) const {
// Loaded pointers default to the pointer bank because they are usually
// dereferenced. When *every* use forces the value onto a GPR (e.g. it only
// feeds a pointer G_SELECT / G_ICMP), keeping it on the pointer bank only
// introduces bridging cross-bank copies. In that case map the loaded value
// directly to the GPR bank instead (mirrors the G_TRUNC/G_PTRTOINT logic).
Register Dst = MI.getOperand(0).getReg();
if (!MRI.getType(Dst).isPointer())
return false;
if (MRI.use_nodbg_empty(Dst))
return false;
return all_of(MRI.use_nodbg_instructions(Dst),
[&](const MachineInstr &UseMI) {
return requiresGPRRegBank(UseMI, MRI, 0);
});
}

bool AIEBaseRegisterBankInfo::requiresPTRRegBank(const MachineInstr &MI,
const MachineRegisterInfo &MRI,
unsigned Depth) const {
Expand Down Expand Up @@ -424,6 +452,13 @@ AIEBaseRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
OpRegBankIdx = {PMI_GPR, PMI_GPR, PMI_GPR, PMI_GPR};
break;
}
case TargetOpcode::G_LOAD: {
// A loaded pointer whose every use forces it onto a GPR is mapped to the
// GPR bank directly, avoiding a ptr->gpr bridging copy.
if (pointerLoadDefShouldBeGPR(MI, MRI))
OpRegBankIdx[0] = PMI_GPR;
break;
}
case TargetOpcode::G_FRAME_INDEX: {
OpRegBankIdx = {PMI_PTR};
break;
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/Target/AIE/AIEBaseRegisterBankInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// (c) Copyright 2023-2024 Advanced Micro Devices, Inc. or its affiliates
// (c) Copyright 2023-2026 Advanced Micro Devices, Inc. or its affiliates
//
//===----------------------------------------------------------------------===//
/// \file
Expand Down Expand Up @@ -91,6 +91,12 @@ class AIEBaseRegisterBankInfo : public RegisterBankInfo {
bool requiresPTRRegBank(const MachineInstr &MI,
const MachineRegisterInfo &MRI, unsigned Depth) const;

/// Return true if \p MI is a load whose pointer-typed result is only ever
/// used by GPR-requiring instructions, so its def should be mapped to the
/// GPR bank instead of the (default) pointer bank.
bool pointerLoadDefShouldBeGPR(const MachineInstr &MI,
const MachineRegisterInfo &MRI) const;

using RegisterUsedAsSpecificBankFcn =
std::function<bool(const MachineInstr &MI, const MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI, Register Reg)>;
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/Target/AIE/aie2ps/AIE2PSRegisterBankInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,11 @@ void AIE2PSRegisterBankInfo::setAIEGenericInstrMapping(
case AIE2PS::G_AIE_OFFSET_SEXTLOAD: {
// Offset is operand #2
OpRegBankIdx[2] = PMI_MOD;
// A loaded pointer whose every use forces it onto a GPR is mapped to the
// GPR bank directly, avoiding a ptr->gpr bridging copy.
if (MI.getOpcode() == AIE2PS::G_AIE_OFFSET_LOAD &&
pointerLoadDefShouldBeGPR(MI, MRI))
OpRegBankIdx[0] = PMI_GPR;
break;
}
case AIE2PS::G_AIE_POSTINC_STORE:
Expand All @@ -1169,6 +1174,13 @@ void AIE2PSRegisterBankInfo::setAIEGenericInstrMapping(
case AIE2PS::G_AIE_POSTINC_SEXTLOAD: {
// Offset is operand #3
OpRegBankIdx[3] = PMI_MOD;
// The loaded value is def #0 (def #1 is the incremented pointer, which
// stays on the pointer bank for addressing). If the loaded pointer value's
// every use forces it onto a GPR, map def #0 to the GPR bank directly to
// avoid a ptr->gpr bridging copy.
if (MI.getOpcode() == AIE2PS::G_AIE_POSTINC_LOAD &&
pointerLoadDefShouldBeGPR(MI, MRI))
OpRegBankIdx[0] = PMI_GPR;
break;
}
case AIE2PS::G_AIE_UNPAD_VECTOR:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
#
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# (c) Copyright 2026 Advanced Micro Devices, Inc. or its affiliates
# RUN: llc -mtriple aie2ps -run-pass=regbankselect -regbankselect-fast %s -verify-machineinstrs -o - | FileCheck %s

# A loaded pointer whose every use forces it onto a GPR (G_ICMP / pointer
# G_SELECT) is mapped to the GPR bank directly, avoiding a ptr->gpr bridging
# copy. When at least one use dereferences the pointer, it stays on the
# pointer bank.

# G_AIE_OFFSET_LOAD result used only by an equality compare -> GPR bank.
---
name: offset_load_only_icmp
legalized: true
body: |
bb.0:
liveins: $p0, $p1, $r0
; CHECK-LABEL: name: offset_load_only_icmp
; CHECK: liveins: $p0, $p1, $r0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:ptrregbank(p0) = COPY $p1
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprregbank(s32) = COPY $r0
; CHECK-NEXT: [[TRUNC:%[0-9]+]]:modregbank(s20) = G_TRUNC [[COPY2]](s32)
; CHECK-NEXT: [[AIE_OFFSET_LOAD:%[0-9]+]]:gprregbank(p0) = G_AIE_OFFSET_LOAD [[COPY]](p0), [[TRUNC]](s20) :: (load (p0), align 4)
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprregbank(p0) = COPY [[COPY1]](p0)
; CHECK-NEXT: [[ICMP:%[0-9]+]]:gprregbank(s32) = G_ICMP intpred(eq), [[AIE_OFFSET_LOAD]](p0), [[COPY3]]
; CHECK-NEXT: $r1 = COPY [[ICMP]](s32)
%0:_(p0) = COPY $p0
%1:_(p0) = COPY $p1
%2:_(s32) = COPY $r0
%3:_(s20) = G_TRUNC %2
%4:_(p0) = G_AIE_OFFSET_LOAD %0, %3 :: (load (p0))
%5:_(s32) = G_ICMP intpred(eq), %4, %1
$r1 = COPY %5
...

# G_AIE_OFFSET_LOAD result used by two compares -> still GPR bank.
---
name: offset_load_two_icmps
legalized: true
body: |
bb.0:
liveins: $p0, $p1, $p2, $r0
; CHECK-LABEL: name: offset_load_two_icmps
; CHECK: liveins: $p0, $p1, $p2, $r0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:ptrregbank(p0) = COPY $p1
; CHECK-NEXT: [[COPY2:%[0-9]+]]:ptrregbank(p0) = COPY $p2
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprregbank(s32) = COPY $r0
; CHECK-NEXT: [[TRUNC:%[0-9]+]]:modregbank(s20) = G_TRUNC [[COPY3]](s32)
; CHECK-NEXT: [[AIE_OFFSET_LOAD:%[0-9]+]]:gprregbank(p0) = G_AIE_OFFSET_LOAD [[COPY]](p0), [[TRUNC]](s20) :: (load (p0), align 4)
; CHECK-NEXT: [[COPY4:%[0-9]+]]:gprregbank(p0) = COPY [[COPY1]](p0)
; CHECK-NEXT: [[ICMP:%[0-9]+]]:gprregbank(s32) = G_ICMP intpred(eq), [[AIE_OFFSET_LOAD]](p0), [[COPY4]]
; CHECK-NEXT: [[COPY5:%[0-9]+]]:gprregbank(p0) = COPY [[COPY2]](p0)
; CHECK-NEXT: [[ICMP1:%[0-9]+]]:gprregbank(s32) = G_ICMP intpred(ne), [[AIE_OFFSET_LOAD]](p0), [[COPY5]]
; CHECK-NEXT: $r1 = COPY [[ICMP]](s32)
; CHECK-NEXT: $r2 = COPY [[ICMP1]](s32)
%0:_(p0) = COPY $p0
%1:_(p0) = COPY $p1
%2:_(p0) = COPY $p2
%3:_(s32) = COPY $r0
%4:_(s20) = G_TRUNC %3
%5:_(p0) = G_AIE_OFFSET_LOAD %0, %4 :: (load (p0))
%6:_(s32) = G_ICMP intpred(eq), %5, %1
%7:_(s32) = G_ICMP intpred(ne), %5, %2
$r1 = COPY %6
$r2 = COPY %7
...

# G_AIE_OFFSET_LOAD result feeding a pointer G_SELECT -> GPR bank.
---
name: offset_load_pointer_select
legalized: true
body: |
bb.0:
liveins: $p0, $p1, $r0
; CHECK-LABEL: name: offset_load_pointer_select
; CHECK: liveins: $p0, $p1, $r0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:ptrregbank(p0) = COPY $p1
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprregbank(s32) = COPY $r0
; CHECK-NEXT: [[TRUNC:%[0-9]+]]:modregbank(s20) = G_TRUNC [[COPY2]](s32)
; CHECK-NEXT: [[AIE_OFFSET_LOAD:%[0-9]+]]:gprregbank(p0) = G_AIE_OFFSET_LOAD [[COPY]](p0), [[TRUNC]](s20) :: (load (p0), align 4)
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprregbank(p0) = COPY [[COPY1]](p0)
; CHECK-NEXT: [[SELECT:%[0-9]+]]:gprregbank(p0) = G_SELECT [[COPY2]](s32), [[AIE_OFFSET_LOAD]], [[COPY3]]
; CHECK-NEXT: $p1 = COPY [[SELECT]](p0)
%0:_(p0) = COPY $p0
%1:_(p0) = COPY $p1
%2:_(s32) = COPY $r0
%3:_(s20) = G_TRUNC %2
%4:_(p0) = G_AIE_OFFSET_LOAD %0, %3 :: (load (p0))
%5:_(p0) = G_SELECT %2, %4, %1
$p1 = COPY %5
...

# G_AIE_POSTINC_LOAD: the loaded value (def #0) feeds a compare -> GPR bank,
# while the incremented pointer (def #1) stays on the pointer bank.
---
name: postinc_load_only_icmp
legalized: true
body: |
bb.0:
liveins: $p0, $p1, $r0
; CHECK-LABEL: name: postinc_load_only_icmp
; CHECK: liveins: $p0, $p1, $r0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:ptrregbank(p0) = COPY $p1
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprregbank(s32) = COPY $r0
; CHECK-NEXT: [[TRUNC:%[0-9]+]]:modregbank(s20) = G_TRUNC [[COPY2]](s32)
; CHECK-NEXT: [[AIE_POSTINC_LOAD:%[0-9]+]]:gprregbank(p0), [[AIE_POSTINC_LOAD1:%[0-9]+]]:ptrregbank(p0) = G_AIE_POSTINC_LOAD [[COPY]], [[TRUNC]](s20) :: (load (p0), align 4)
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprregbank(p0) = COPY [[COPY1]](p0)
; CHECK-NEXT: [[ICMP:%[0-9]+]]:gprregbank(s32) = G_ICMP intpred(eq), [[AIE_POSTINC_LOAD]](p0), [[COPY3]]
; CHECK-NEXT: $r1 = COPY [[ICMP]](s32)
; CHECK-NEXT: $p2 = COPY [[AIE_POSTINC_LOAD1]](p0)
%0:_(p0) = COPY $p0
%1:_(p0) = COPY $p1
%2:_(s32) = COPY $r0
%3:_(s20) = G_TRUNC %2
%4:_(p0), %5:_(p0) = G_AIE_POSTINC_LOAD %0, %3 :: (load (p0))
%6:_(s32) = G_ICMP intpred(eq), %4, %1
$r1 = COPY %6
$p2 = COPY %5
...

# Plain G_LOAD result used only by a compare -> GPR bank (base-class path).
---
name: plain_load_only_icmp
legalized: true
body: |
bb.0:
liveins: $p0, $p1
; CHECK-LABEL: name: plain_load_only_icmp
; CHECK: liveins: $p0, $p1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:ptrregbank(p0) = COPY $p1
; CHECK-NEXT: [[LOAD:%[0-9]+]]:gprregbank(p0) = G_LOAD [[COPY]](p0) :: (load (p0), align 4)
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprregbank(p0) = COPY [[COPY1]](p0)
; CHECK-NEXT: [[ICMP:%[0-9]+]]:gprregbank(s32) = G_ICMP intpred(eq), [[LOAD]](p0), [[COPY2]]
; CHECK-NEXT: $r1 = COPY [[ICMP]](s32)
%0:_(p0) = COPY $p0
%1:_(p0) = COPY $p1
%2:_(p0) = G_LOAD %0 :: (load (p0))
%3:_(s32) = G_ICMP intpred(eq), %2, %1
$r1 = COPY %3
...

# Negative test: a mixed use (compare + dereference) keeps the loaded pointer
# on the pointer bank, since not every use forces it onto a GPR.
---
name: offset_load_mixed_use_stays_ptr
legalized: true
body: |
bb.0:
liveins: $p0, $p1, $r0
; CHECK-LABEL: name: offset_load_mixed_use_stays_ptr
; CHECK: liveins: $p0, $p1, $r0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:ptrregbank(p0) = COPY $p1
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprregbank(s32) = COPY $r0
; CHECK-NEXT: [[TRUNC:%[0-9]+]]:modregbank(s20) = G_TRUNC [[COPY2]](s32)
; CHECK-NEXT: [[AIE_OFFSET_LOAD:%[0-9]+]]:ptrregbank(p0) = G_AIE_OFFSET_LOAD [[COPY]](p0), [[TRUNC]](s20) :: (load (p0), align 4)
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprregbank(p0) = COPY [[AIE_OFFSET_LOAD]](p0)
; CHECK-NEXT: [[COPY4:%[0-9]+]]:gprregbank(p0) = COPY [[COPY1]](p0)
; CHECK-NEXT: [[ICMP:%[0-9]+]]:gprregbank(s32) = G_ICMP intpred(eq), [[COPY3]](p0), [[COPY4]]
; CHECK-NEXT: [[AIE_OFFSET_LOAD1:%[0-9]+]]:gprregbank(s32) = G_AIE_OFFSET_LOAD [[AIE_OFFSET_LOAD]](p0), [[TRUNC]](s20) :: (load (s32))
; CHECK-NEXT: $r1 = COPY [[ICMP]](s32)
; CHECK-NEXT: $r2 = COPY [[AIE_OFFSET_LOAD1]](s32)
%0:_(p0) = COPY $p0
%1:_(p0) = COPY $p1
%2:_(s32) = COPY $r0
%3:_(s20) = G_TRUNC %2
%4:_(p0) = G_AIE_OFFSET_LOAD %0, %3 :: (load (p0))
%5:_(s32) = G_ICMP intpred(eq), %4, %1
%6:_(s32) = G_AIE_OFFSET_LOAD %4, %3 :: (load (s32))
$r1 = COPY %5
$r2 = COPY %6
...

# Negative test: a loaded pointer that is dereferenced stays on the pointer
# bank.
---
name: offset_load_deref_stays_ptr
legalized: true
body: |
bb.0:
liveins: $p0, $r0
; CHECK-LABEL: name: offset_load_deref_stays_ptr
; CHECK: liveins: $p0, $r0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:ptrregbank(p0) = COPY $p0
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprregbank(s32) = COPY $r0
; CHECK-NEXT: [[TRUNC:%[0-9]+]]:modregbank(s20) = G_TRUNC [[COPY1]](s32)
; CHECK-NEXT: [[AIE_OFFSET_LOAD:%[0-9]+]]:ptrregbank(p0) = G_AIE_OFFSET_LOAD [[COPY]](p0), [[TRUNC]](s20) :: (load (p0), align 4)
; CHECK-NEXT: [[AIE_OFFSET_LOAD1:%[0-9]+]]:gprregbank(s32) = G_AIE_OFFSET_LOAD [[AIE_OFFSET_LOAD]](p0), [[TRUNC]](s20) :: (load (s32))
; CHECK-NEXT: $r1 = COPY [[AIE_OFFSET_LOAD1]](s32)
%0:_(p0) = COPY $p0
%1:_(s32) = COPY $r0
%2:_(s20) = G_TRUNC %1
%3:_(p0) = G_AIE_OFFSET_LOAD %0, %2 :: (load (p0))
%4:_(s32) = G_AIE_OFFSET_LOAD %3, %2 :: (load (s32))
$r1 = COPY %4
...
Loading