llvm/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll

; RUN: llc < %s -mtriple s390x-ibm-zos | FileCheck %s
; Source to regenerate:
; struct Foo {
;   int * __ptr32 p32;
;   int *p64;
;   char *cp64;
; };
; void use_foo(Foo *f);
;
; // Assiging a ptr32 value to a 64-bit pointer
; void ptr32_to_ptr(Foo *f, int * __ptr32 i) {
;   f->p64 = i;
;   use_foo(f);
; }
;
; // Assigning a 64-bit ptr value to a ptr32
; void ptr_to_ptr32(Foo *f, int *i) {
;   f->p32 = i;
;   use_foo(f);
; }
;
; // Assigning a ptr32 value to a ptr32 value
; void ptr32_to_ptr32(Foo *f, int * __ptr32 i) {
;   f->p32 = i;
;   use_foo(f);
; }
;
; void ptr_to_ptr(Foo *f, int *i) {
;   f->p64 = i;
;   use_foo(f);
; }
;
; void test_indexing(Foo *f) {
;  f->cp64 = ((char * __ptr32 *)1028)[1];
;  use_foo(f);
; }
;
; void test_indexing_2(Foo *f) {
;   f->cp64 = ((char *** __ptr32 *)1028)[1][2][3];
;   use_foo(f);
; }
;
; unsigned long* test_misc() {
;   unsigned long* x = (unsigned long*)((char***** __ptr32*)1208)[0][11][1][113][149];
;   return x;
; }
;
; char* __ptr32* __ptr32 test_misc_2() {
;   static char* __ptr32* __ptr32 res = 0;
;   if (res == 0) {
;     res = ((char* __ptr32* __ptr32* __ptr32* __ptr32*)0)[4][136][6];
;   }
;   return res;
; }
;
; unsigned short test_misc_3() {
;   unsigned short this_asid = ((unsigned short*)(*(char* __ptr32*)(0x224)))[18];
;   return this_asid;
; }
;
; int test_misc_4() {
;   int a = (*(int*)(80 + ((char**** __ptr32*)1208)[0][11][1][123]) > 0x040202FF);
;   return a;
; }
;
; void test_misc_5(struct Foo *f) {
;   f->cp64  = *(char* __ptr32 *)(PSA_PTR + PSAAOLD);
;   use_foo(f);
; }
;
; int get_processor_count() {
;  return ((char * __ptr32 * __ptr32 *)0)[4][165][53];
; }
;
; void spill_ptr32_args_to_registers( char *__ptr32 p ) {
;   void g ( int, ... );
;   g ( 5, p, p, p, p, p );
; }
;
; $ clang -cc1 -triple s390x-ibm-zos -fzos-extensions -O2 -S t.cpp
;
; For the last test case:
;
;#include <stdlib.h>
;
;int foo();
;
;typedef struct qa_area {/* Area descriptor                */
;  char* __ptr32 text;           /* Start address of area          */
;  int length;      /* Size of area in bytes          */
;} qa_area;
;
;int main() {
;  qa_area* __ptr32 fap_asm_option_a = (qa_area*)__malloc31(sizeof(qa_area));
;
;  //((qa_area*)fap_asm_option_a)->length   = foo(); //PASSES
;  fap_asm_option_a->length = foo();                 //CRASHES
;  return 0;
;}

%struct.Foo = type { ptr addrspace(1), ptr, ptr }
declare void @use_foo(ptr)

define void @ptr32_to_ptr(ptr %f, ptr addrspace(1) %i) {
entry:
; CHECK-LABEL: ptr32_to_ptr:
; CHECK:       llgtr 0, 2
; CHECK-NEXT:  stg   0, 8(1)
  %0 = addrspacecast ptr addrspace(1) %i to ptr
  %p64 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 1
  store ptr %0, ptr %p64, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define void @ptr_to_ptr32(ptr %f, ptr %i) {
entry:
; CHECK-LABEL: ptr_to_ptr32:
; CHECK:       nilh 2, 32767
; CHECK-NEXT:  st   2, 0(1)
  %0 = addrspacecast ptr %i to ptr addrspace(1)
  %p32 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 0
  store ptr addrspace(1) %0, ptr %p32, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define void @ptr32_to_ptr32(ptr %f, ptr addrspace(1) %i) {
entry:
; CHECK-LABEL: ptr32_to_ptr32:
; CHECK:       st 2, 0(1)
  %p32 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 0
  store ptr addrspace(1) %i, ptr %p32, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define void @ptr_to_ptr(ptr %f, ptr %i) {
; CHECK-LABEL: ptr_to_ptr:
; CHECK:       stg 2, 8(1)
  %p64 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 1
  store ptr %i, ptr %p64, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define void @test_indexing(ptr %f) {
entry:
; CHECK-LABEL: test_indexing:
; CHECK:       l     0, 1032
; CHECK:       llgtr 0, 0
; CHECK:       stg   0, 16(1)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 1032 to ptr), align 8
  %1 = addrspacecast ptr addrspace(1) %0 to ptr
  %cp64 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 2
  store ptr %1, ptr %cp64, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define void @test_indexing_2(ptr %f) {
entry:
; CHECK-LABEL: test_indexing_2:
; CHECK:       lhi   0, 16
; CHECK-NEXT:  a     0, 1032
; CHECK-NEXT:  llgtr 2, 0
; CHECK:       lg    0, 24(2)
; CHECK:       stg   0, 16(1)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 1032 to ptr), align 8
  %arrayidx = getelementptr inbounds ptr, ptr addrspace(1) %0, i32 2
  %1 = load ptr, ptr addrspace(1) %arrayidx, align 8
  %arrayidx1 = getelementptr inbounds ptr, ptr %1, i64 3
  %2 = bitcast ptr %arrayidx1 to ptr
  %3 = load i64, ptr %2, align 8
  %cp64 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 2
  %4 = bitcast ptr %cp64 to ptr
  store i64 %3, ptr %4, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define ptr @test_misc() {
entry:
; CHECK-LABEL: test_misc:
; CHECK:       lhi   0, 88
; CHECK-NEXT:  a     0, 1208
; CHECK-NEXT:  llgtr 1, 0
; CHECK-NEXT:  lg    1, 0(1)
; CHECK-NEXT:  lg    1, 8(1)
; CHECK-NEXT:  lg    1, 904(1)
; CHECK-NEXT:  lg    3, 1192(1)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 1208 to ptr), align 8
  %arrayidx = getelementptr inbounds ptr, ptr addrspace(1) %0, i32 11
  %1 = load ptr, ptr addrspace(1) %arrayidx, align 8
  %arrayidx1 = getelementptr inbounds ptr, ptr %1, i64 1
  %2 = load ptr, ptr %arrayidx1, align 8
  %arrayidx2 = getelementptr inbounds ptr, ptr %2, i64 113
  %3 = load ptr, ptr %arrayidx2, align 8
  %arrayidx3 = getelementptr inbounds ptr, ptr %3, i64 149
  %4 = bitcast ptr %arrayidx3 to ptr
  %5 = load ptr, ptr %4, align 8
  ret ptr %5
}

define ptr addrspace(1) @test_misc_2() {
entry:
; CHECK-LABEL: test_misc_2:
; CHECK:       lhi   0, 544
; CHECK:       a     0, 16
; CHECK:       llgtr 1, 0
; CHECK:       lhi   0, 24
; CHECK:       a     0, 0(1)
; CHECK:       llgtr 1, 0
  %0 = load ptr addrspace(1), ptr inttoptr (i64 16 to ptr), align 16
  %arrayidx = getelementptr inbounds ptr addrspace(1), ptr addrspace(1) %0, i32 136
  %1 = load ptr addrspace(1), ptr addrspace(1) %arrayidx, align 4
  %arrayidx1 = getelementptr inbounds ptr addrspace(1), ptr addrspace(1) %1, i32 6
  %2 = load ptr addrspace(1), ptr addrspace(1) %arrayidx1, align 4
  ret ptr addrspace(1) %2
}

define zeroext i16 @test_misc_3() {
entry:
; CHECK-LABEL: test_misc_3:
; CHECK:       a     0, 548
; CHECK-NEXT:  llgtr 1, 0
; CHECK-NEXT:  llgh  3, 0(1)
; CHECK-NEXT:  b     2(7)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 548 to ptr), align 4
  %arrayidx2 = getelementptr inbounds i16, ptr addrspace(1) %0, i32 18
  %arrayidx = addrspacecast ptr addrspace(1) %arrayidx2 to ptr
  %1 = load i16, ptr %arrayidx, align 2
  ret i16 %1
}

define signext i32 @test_misc_4() {
entry:
; CHECK-LABEL: test_misc_4:
; CHECK:       lhi   0, 88
; CHECK-NEXT:  a     0, 1208
; CHECK-NEXT:  llgtr 1, 0
; CHECK-NEXT:  lg    1, 0(1)
; CHECK-NEXT:  lg    1, 8(1)
; CHECK-NEXT:  lg    1, 984(1)
; CHECK-NEXT:  iilf  0, 67240703
; CHECK-NEXT:  c     0, 80(1)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 1208 to ptr), align 8
  %arrayidx = getelementptr inbounds ptr, ptr addrspace(1) %0, i32 11
  %1 = load ptr, ptr addrspace(1) %arrayidx, align 8
  %arrayidx1 = getelementptr inbounds ptr, ptr %1, i64 1
  %2 = load ptr, ptr %arrayidx1, align 8
  %arrayidx2 = getelementptr inbounds ptr, ptr %2, i64 123
  %3 = load ptr, ptr %arrayidx2, align 8
  %add.ptr = getelementptr inbounds i8, ptr %3, i64 80
  %4 = bitcast ptr %add.ptr to ptr
  %5 = load i32, ptr %4, align 4
  %cmp = icmp sgt i32 %5, 67240703
  %conv = zext i1 %cmp to i32
  ret i32 %conv
}

define void @test_misc_5(ptr %f) {
entry:
; CHECK-LABEL: test_misc_5:
; CHECK:       l     0, 548
; CHECK-NEXT:  lg  6, 8(5)
; CHECK-NEXT:  lg  5, 0(5)
; CHECK-NEXT:  llgtr 0, 0
; CHECK-NEXT:  stg   0, 16(1)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 548 to ptr), align 4
  %1 = addrspacecast ptr addrspace(1) %0 to ptr
  %cp64 = getelementptr inbounds %struct.Foo, ptr %f, i64 0, i32 2
  store ptr %1, ptr %cp64, align 8
  tail call void @use_foo(ptr %f)
  ret void
}

define signext i32 @get_processor_count() {
entry:
; CHECK-LABEL: get_processor_count:
; CHECK: lhi 0, 660
; CHECK-NEXT: a 0, 16
; CHECK-NEXT: llgtr 1, 0
; CHECK-NEXT: lhi 0, 53
; CHECK-NEXT: a 0, 0(1)
; CHECK-NEXT: llgtr 1, 0
; CHECK-NEXT: lgb 3, 0(1)
  %0 = load ptr addrspace(1), ptr inttoptr (i64 16 to ptr), align 16
  %arrayidx = getelementptr inbounds ptr addrspace(1), ptr addrspace(1) %0, i32 165
  %1 = load ptr addrspace(1), ptr addrspace(1) %arrayidx, align 4
  %arrayidx1 = getelementptr inbounds i8, ptr addrspace(1) %1, i32 53
  %2 = load i8, ptr addrspace(1) %arrayidx1, align 1
  %conv = sext i8 %2 to i32
  ret i32 %conv
}

define void @spill_ptr32_args_to_registers(i8 addrspace(1)* %p) {
entry:
; CHECK-LABEL: spill_ptr32_args_to_registers:
; CHECK:         stmg 6, 7, 1872(4)
; CHECK-NEXT:    aghi 4, -192
; CHECK-NEXT:    lgr 2, 1
; CHECK-NEXT:    lg 6, 24(5)
; CHECK-NEXT:    lg 5, 16(5)
; CHECK-NEXT:    stg 1, 2216(4)
; CHECK-NEXT:    stg 1, 2208(4)
; CHECK-NEXT:    lghi 1, 5
; CHECK-NEXT:    stg 2, 2200(4)
; CHECK-NEXT:    lgr 3, 2
; CHECK-NEXT:    basr 7, 6
; CHECK-NEXT:    bcr 0, 0
; CHECK-NEXT:    lg 7, 2072(4)
; CHECK-NEXT:    aghi 4, 192
; CHECK-NEXT:    b 2(7)
  tail call void (i32, ...) @g(i32 noundef signext 5, ptr addrspace(1) noundef %p, ptr addrspace(1) noundef %p, ptr addrspace(1) noundef %p, ptr addrspace(1) noundef %p, ptr addrspace(1) noundef %p)
  ret void
}
declare void @g(i32 signext, ...)

; The resulting instructions may look odd on first view but it is a result of
; the C code. __malloc31() returns a 64 bit pointer, thus the sequence
;
;        la      1, 4(8)
;        llgtr   1, 1
;
; references the length attribute via the 64 bit pointer, and performs the
; cast to __ptr32, setting the upper 32 bit to zero.
;
define signext i32 @setlength() {
; CHECK-LABEL: setlength:
; CHECK: basr    7, 6
; CHECK: lgr     [[MALLOC:[0-9]+]], 3
; CHECK: basr    7, 6
; CHECK: lgr     [[LENGTH:[0-9]+]], 3
; CHECK: la      [[ADDR:[0-9]+]], 4([[MALLOC]])
; CHECK: llgtr   [[ADDR]], [[ADDR]]
; CHECK: stg     [[LENGTH]], 0([[ADDR]])
entry:
  %call = tail call ptr @__malloc31(i64 noundef 8)
  %call1 = tail call signext i32 @foo()
  %length = getelementptr inbounds i8, ptr %call, i64 4
  %0 = bitcast ptr %length to ptr
  %1 = addrspacecast ptr %0 to ptr addrspace(1)
  store i32 %call1, ptr addrspace(1) %1, align 4
  ret i32 0
}

; Same as test before, but this time calling
;  extern char* __ptr32 domalloc(unsigned long);
; instead of __malloc31(). Note the different instruction sequence, because
; the function now returns a __ptr32.
;
define signext i32 @setlength2() {
; CHECK-LABEL: setlength2:
; CHECK: basr    7, 6
; CHECK: lgr     [[MALLOC:[0-9]+]], 3
; CHECK: basr    7, 6
; CHECK: lgr     [[LENGTH:[0-9]+]], 3
; CHECK: ahi     [[MALLOC]], 4
; CHECK: llgtr   [[ADDR]], [[MALLOC]]
; CHECK: stg     [[LENGTH]], 0([[ADDR]])
entry:
  %call = tail call ptr addrspace(1) @domalloc(i64 noundef 8)
  %call1 = tail call signext i32 @foo()
  %length = getelementptr inbounds i8, ptr addrspace(1) %call, i32 4
  %0 = bitcast ptr addrspace(1) %length to ptr addrspace(1)
  store i32 %call1, ptr addrspace(1) %0, align 4
  ret i32 0
}

declare ptr @__malloc31(i64)

declare signext i32 @foo(...)

declare ptr addrspace(1) @domalloc(i64)