1; RUN: llc -mtriple=thumbv8m.base-eabi -mattr=+execute-only %s -o - | FileCheck --check-prefixes=CHECK,CHECK-MOVW %s 2; RUN: llc -mtriple=thumbv6m-eabi -mattr=+execute-only %s -o - | FileCheck --check-prefixes=CHECK,CHECK-NOMOVW %s 3 4; Largest offset that fits into sp-relative ldr 5; CHECK-LABEL: ldr_range_end: 6; CHECK: ldr {{r[0-9]+}}, [sp, #1020] 7define i32 @ldr_range_end() { 8entry: 9 %var = alloca i32, align 4 10 %arr = alloca [1020 x i8], align 4 11 %0 = load i32, ptr %var, align 4 12 ret i32 %0 13} 14 15; Smallest offset that fits into add+ldr 16; CHECK-LABEL: add_ldr_range_start: 17; CHECK: add [[REG:r[0-9]+]], sp, #900 18; CHECK-NEXT: ldr {{r[0-9]+}}, [[[REG]], #124] 19define i32 @add_ldr_range_start() { 20entry: 21 %var = alloca i32, align 4 22 %arr = alloca [1024 x i8], align 4 23 %0 = load i32, ptr %var, align 4 24 ret i32 %0 25} 26 27; Largest offset that fits into add+ldr 28; CHECK-LABEL: add_ldr_range_end: 29; CHECK: add [[REG:r[0-9]+]], sp, #1020 30; CHECK-NEXT: ldr {{r[0-9]+}}, [[[REG]], #124] 31define i32 @add_ldr_range_end() { 32entry: 33 %var = alloca i32, align 4 34 %arr = alloca [1144 x i8], align 4 35 %0 = load i32, ptr %var, align 4 36 ret i32 %0 37} 38 39; Smallest offset where we start using mov32. If we don't have movw then using 40; an ldr offset means we save an add. 41; CHECK-LABEL: mov32_range_start: 42; CHECK-MOVW: movw [[REG:r[0-9]+]], #1148 43; CHECK-NOMOVW: movs [[REG:r[0-9]+]], #4 44; CHECK-NOMOVW-NEXT: lsls [[REG]], [[REG]], #8 45; CHECK-NEXT: add [[REG]], sp 46; CHECK-MOVW-NEXT: ldr {{r[0-9]+}}, [[[REG]]] 47; CHECK-NOMOVW-NEXT: ldr {{r[0-9]+}}, [[[REG]], #124] 48define i32 @mov32_range_start() { 49entry: 50 %var = alloca i32, align 4 51 %arr = alloca [1148 x i8], align 4 52 %0 = load i32, ptr %var, align 4 53 ret i32 %0 54} 55 56; Here using an ldr offset doesn't save an add so we shouldn't do it. 57; CHECK-LABEL: mov32_range_next: 58; CHECK-MOVW: movw [[REG:r[0-9]+]], #1152 59; CHECK-NOMOVW: movs [[REG:r[0-9]+]], #4 60; CHECK-NOMOVW-NEXT: lsls [[REG]], [[REG]], #8 61; CHECK-NOMOVW-NEXT: adds [[REG]], #128 62; CHECK-NEXT: add [[REG]], sp 63; CHECK-NEXT: ldr {{r[0-9]+}}, [[[REG]]] 64define i32 @mov32_range_next() { 65entry: 66 %var = alloca i32, align 4 67 %arr = alloca [1152 x i8], align 4 68 %0 = load i32, ptr %var, align 4 69 ret i32 %0 70} 71 72; Smallest offset where using an ldr offset prevents needing a movt or lsl+add 73; CHECK-LABEL: can_clear_top_byte_start: 74; CHECK: add sp, {{r[0-9]+}} 75; CHECK-MOVW: movw [[REG:r[0-9]+]], #65412 76; CHECK-NOMOVW: movs [[REG:r[0-9]+]], #255 77; CHECK-NOMOVW-NEXT: lsls [[REG:r[0-9]+]], [[REG:r[0-9]+]], #8 78; CHECK-NOMOVW-NEXT: adds [[REG:r[0-9]+]], #132 79; CHECK-NEXT: add [[REG]], sp 80; CHECK-NEXT: ldr {{r[0-9]+}}, [[[REG]], #124] 81define i32 @can_clear_top_byte_start() { 82entry: 83 %var = alloca i32, align 4 84 %arr = alloca [65536 x i8], align 4 85 %0 = load i32, ptr %var, align 4 86 ret i32 %0 87} 88 89; Largest offset where using an ldr offset prevents needing a movt or lsl+add 90; CHECK-LABEL: can_clear_top_byte_end: 91; CHECK: add sp, {{r[0-9]+}} 92; CHECK-MOVW: movw [[REG:r[0-9]+]], #65532 93; CHECK-NOMOVW: movs [[REG:r[0-9]+]], #255 94; CHECK-NOMOVW-NEXT: lsls [[REG:r[0-9]+]], [[REG:r[0-9]+]], #8 95; CHECK-NOMOVW-NEXT: adds [[REG:r[0-9]+]], #252 96; CHECK-NEXT: add [[REG]], sp 97; CHECK-NEXT: ldr {{r[0-9]+}}, [[[REG]], #124] 98define i32 @can_clear_top_byte_end() { 99entry: 100 %var = alloca i32, align 4 101 %arr = alloca [65656 x i8], align 4 102 %0 = load i32, ptr %var, align 4 103 ret i32 %0 104} 105 106; Smallest offset where using an ldr offset doesn't clear the top byte, though 107; we can use an ldr offset if not using movt to save an add of the low byte. 108; CHECK-LABEL: cant_clear_top_byte_start: 109; CHECK: add sp, {{r[0-9]+}} 110; CHECK-MOVW: movw [[REG:r[0-9]+]], #124 111; CHECK-MOVW-NEXT: movt [[REG:r[0-9]+]], #1 112; CHECK-NOMOVW: movs [[REG:r[0-9]+]], #1 113; CHECK-NOMOVW-NEXT: lsls [[REG:r[0-9]+]], [[REG:r[0-9]+]], #16 114; CHECK-NEXT: add [[REG]], sp 115; CHECK-MOVW-NEXT: ldr {{r[0-9]+}}, [[[REG]]] 116; CHECK-NOMOVW-NEXT: ldr {{r[0-9]+}}, [[[REG]], #124] 117define i32 @cant_clear_top_byte_start() { 118entry: 119 %var = alloca i32, align 4 120 %arr = alloca [65660 x i8], align 4 121 %0 = load i32, ptr %var, align 4 122 ret i32 %0 123} 124 125; An ldr offset doesn't help for anything, so we shouldn't do it. 126; CHECK-LABEL: cant_clear_top_byte_next: 127; CHECK: add sp, {{r[0-9]+}} 128; CHECK-MOVW: movw [[REG:r[0-9]+]], #128 129; CHECK-MOVW: movt [[REG:r[0-9]+]], #1 130; CHECK-NOMOVW: movs [[REG:r[0-9]+]], #1 131; CHECK-NOMOVW-NEXT: lsls [[REG:r[0-9]+]], [[REG:r[0-9]+]], #16 132; CHECK-NOMOVW-NEXT: adds [[REG:r[0-9]+]], #128 133; CHECK-NEXT: add [[REG]], sp 134; CHECK-NEXT: ldr {{r[0-9]+}}, [[[REG]]] 135define i32 @cant_clear_top_byte_next() { 136entry: 137 %var = alloca i32, align 4 138 %arr = alloca [65664 x i8], align 4 139 %0 = load i32, ptr %var, align 4 140 ret i32 %0 141} 142