; Pin: both halves of an i32 load or store through a far pointer address ; the pointer's base with the field offset in Y. The high half of an ; i32 load at `p + K` is a load at `(p + K) + 2`; peeling only the outer ; +2 left it addressed through the computed `p + K` (an i32 add and a ; staging of its result into $E0 / $E2 per access). The i32 store path ; did not peel at all: its high half always took `p + 2` through an i32 ; add. A `+ K` whose constant was already split into halves by ; LowerI32Constant now peels too. CoreMark's linked-list code ; (list->info, list->next) went -7% total time with this. ; ; RUN: llc -mtriple=w65816 -O2 < %s | FileCheck %s target datalayout = "e-m:e-p:32:16-i16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16" ; The pointer field at offset 4: low half at Y=4, high half at Y=6, no ; carry chain (the `adc #0x0` of an i32 address add). define ptr @getInfo(ptr %n) { ; CHECK-LABEL: getInfo: ; CHECK-NOT: adc #0x0 ; CHECK-DAG: ldy #0x4 ; CHECK-DAG: ldy #0x6 ; CHECK-NOT: adc #0x0 ; CHECK: rtl %f = getelementptr inbounds i8, ptr %n, i32 4 %v = load ptr, ptr %f, align 2 ret ptr %v } ; An i32 store at offset 8: Y=8 then Y=10 off the incoming pointer. define void @setCnt(ptr %n, i32 %v) { ; CHECK-LABEL: setCnt: ; CHECK-NOT: adc #0x0 ; CHECK: ldy #0x8 ; CHECK-NEXT: sta [0x{{[0-9a-f]+}}], y ; CHECK-NOT: adc #0x0 ; CHECK: ldy #0xa ; CHECK-NEXT: sta [0x{{[0-9a-f]+}}], y ; CHECK-NOT: adc #0x0 ; CHECK: rtl %f = getelementptr inbounds i8, ptr %n, i32 8 store i32 %v, ptr %f, align 2 ret void } ; An i32 store at offset 0: the high half is Y=2 off the same base. define void @setFirst(ptr %n, i32 %v) { ; CHECK-LABEL: setFirst: ; CHECK-NOT: adc #0x0 ; CHECK: ldy #0x2 ; CHECK-NOT: adc #0x0 ; CHECK: rtl store i32 %v, ptr %n, align 2 ret void } ; A read-modify-write of an i32 element (CoreMark's transition_count[i]++): ; four [p],Y accesses at Y=8 / Y=10, the only add the increment's. define void @bump(ptr %tc) { ; CHECK-LABEL: bump: ; CHECK-NOT: adc #0x2 ; CHECK-NOT: adc #0x8 ; CHECK-DAG: ldy #0x8 ; CHECK-DAG: ldy #0xa ; CHECK-NOT: adc #0x2 ; CHECK-NOT: adc #0x8 ; CHECK: rtl %p = getelementptr inbounds i8, ptr %tc, i32 8 %v = load i32, ptr %p, align 2 %w = add i32 %v, 1 store i32 %w, ptr %p, align 2 ret void } ; Offsets that no longer fit Y fall back to the i32 add. define void @setFar(ptr %n, i32 %v) { ; CHECK-LABEL: setFar: ; CHECK: adc #0xfffe ; CHECK: rtl %f = getelementptr inbounds i8, ptr %n, i32 65534 store i32 %v, ptr %f, align 2 ret void }