diff options
author | Evgeniy Stepanov <eugeni.stepanov@gmail.com> | 2016-06-29 20:37:43 +0000 |
---|---|---|
committer | Evgeniy Stepanov <eugeni.stepanov@gmail.com> | 2016-06-29 20:37:43 +0000 |
commit | a5da256f9225b8a1fc4010a3c8101d91fba264bd (patch) | |
tree | 543dc475c2b597ef86defcf3ebdadc87db93e872 /llvm/test/Transforms | |
parent | b9e053cfd716434b5b3df2024709a498a381f816 (diff) | |
download | bcm5719-llvm-a5da256f9225b8a1fc4010a3c8101d91fba264bd.tar.gz bcm5719-llvm-a5da256f9225b8a1fc4010a3c8101d91fba264bd.zip |
StackColoring for SafeStack.
This is a fix for PR27842.
An IR-level implementation of stack coloring tailored to work with
SafeStack. It is a bit weaker than the MI implementation in that it
does not the "lifetime start at first access" logic. This can be
improved in the future.
This patch also replaces the naive implementation of stack frame
layout with a greedy algorithm that can split existing stack slots
and even fit small objects inside the alignment padding of other
objects.
llvm-svn: 274162
Diffstat (limited to 'llvm/test/Transforms')
-rw-r--r-- | llvm/test/Transforms/SafeStack/coloring.ll | 44 | ||||
-rw-r--r-- | llvm/test/Transforms/SafeStack/coloring2.ll | 482 |
2 files changed, 526 insertions, 0 deletions
diff --git a/llvm/test/Transforms/SafeStack/coloring.ll b/llvm/test/Transforms/SafeStack/coloring.ll new file mode 100644 index 00000000000..3ed9ccb43f3 --- /dev/null +++ b/llvm/test/Transforms/SafeStack/coloring.ll @@ -0,0 +1,44 @@ +; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s +; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s + +define void @f() safestack { +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16 + + %x = alloca i32, align 4 + %x1 = alloca i32, align 4 + %x2 = alloca i32, align 4 + %0 = bitcast i32* %x to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + +; CHECK: %[[A1:.*]] = getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: %[[A2:.*]] = bitcast i8* %[[A1]] to i32* +; CHECK: call void @capture(i32* nonnull %[[A2]]) + + call void @capture(i32* nonnull %x) + call void @llvm.lifetime.end(i64 4, i8* %0) + %1 = bitcast i32* %x1 to i8* + call void @llvm.lifetime.start(i64 4, i8* %1) + +; CHECK: %[[B1:.*]] = getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: %[[B2:.*]] = bitcast i8* %[[B1]] to i32* +; CHECK: call void @capture(i32* nonnull %[[B2]]) + + call void @capture(i32* nonnull %x1) + call void @llvm.lifetime.end(i64 4, i8* %1) + %2 = bitcast i32* %x2 to i8* + call void @llvm.lifetime.start(i64 4, i8* %2) + +; CHECK: %[[C1:.*]] = getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: %[[C2:.*]] = bitcast i8* %[[C1]] to i32* +; CHECK: call void @capture(i32* nonnull %[[C2]]) + + call void @capture(i32* nonnull %x2) + call void @llvm.lifetime.end(i64 4, i8* %2) + ret void +} + +declare void @llvm.lifetime.start(i64, i8* nocapture) +declare void @llvm.lifetime.end(i64, i8* nocapture) +declare void @capture(i32*) diff --git a/llvm/test/Transforms/SafeStack/coloring2.ll b/llvm/test/Transforms/SafeStack/coloring2.ll new file mode 100644 index 00000000000..54ed812cfe2 --- /dev/null +++ b/llvm/test/Transforms/SafeStack/coloring2.ll @@ -0,0 +1,482 @@ +; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s +; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s + +; x and y share the stack slot. +define void @f() safestack { +; CHECK-LABEL: define void @f +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK: getelementptr i8, i8* %[[USP]], i32 -16 + + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %z = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + %z0 = bitcast i32* %z to i8* + + call void @llvm.lifetime.start(i64 -1, i8* %z0) + call void @llvm.lifetime.start(i64 -1, i8* %x0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 + call void @capture32(i32* %x) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + call void @llvm.lifetime.start(i64 -1, i8* %y0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 + call void @capture32(i32* %y) + call void @llvm.lifetime.end(i64 -1, i8* %y0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 + call void @capture32(i32* %z) + call void @llvm.lifetime.end(i64 -1, i8* %z0) + + ret void +} + +define void @no_markers() safestack { +; CHECK-LABEL: define void @no_markers( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK: getelementptr i8, i8* %[[USP]], i32 -16 + + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + + call void @llvm.lifetime.start(i64 -1, i8* %x0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 + call void @capture32(i32* %x) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 + call void @capture32(i32* %y) + + ret void +} + +; x and y can't share memory, but they can split z's storage. +define void @g() safestack { +; CHECK-LABEL: define void @g +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK: getelementptr i8, i8* %[[USP]], i32 -16 + + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %z = alloca i64, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + %z0 = bitcast i64* %z to i8* + + call void @llvm.lifetime.start(i64 -1, i8* %x0) + call void @llvm.lifetime.start(i64 -1, i8* %y0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 + call void @capture32(i32* %x) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 + call void @capture32(i32* %y) + call void @llvm.lifetime.end(i64 -1, i8* %y0) + call void @llvm.lifetime.start(i64 -1, i8* %z0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 + call void @capture64(i64* %z) + call void @llvm.lifetime.end(i64 -1, i8* %z0) + + ret void +} + +; Both y and z fit in x's alignment gap. +define void @h() safestack { +; CHECK-LABEL: define void @h +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK: getelementptr i8, i8* %[[USP]], i32 -16 + + %x = alloca i32, align 16 + %z = alloca i64, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + %z0 = bitcast i64* %z to i8* + + call void @llvm.lifetime.start(i64 -1, i8* %x0) + call void @llvm.lifetime.start(i64 -1, i8* %y0) + call void @llvm.lifetime.start(i64 -1, i8* %z0) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -16 + call void @capture32(i32* %x) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -12 + call void @capture32(i32* %y) + +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 + call void @capture64(i64* %z) + + call void @llvm.lifetime.end(i64 -1, i8* %x0) + call void @llvm.lifetime.end(i64 -1, i8* %y0) + call void @llvm.lifetime.end(i64 -1, i8* %z0) + + ret void +} + +; void f(bool a, bool b) { +; long x1, x2; capture64(&x1); capture64(&x2); +; if (a) { +; long y; capture64(&y); +; if (b) { +; long y1; capture64(&y1); +; } else { +; long y2; capture64(&y2); +; } +; } else { +; long z; capture64(&z); +; if (b) { +; long z1; capture64(&z1); +; } else { +; long z2; capture64(&z2); +; } +; } +; } +; Everything fits in 4 x 64-bit slots. +define void @i(i1 zeroext %a, i1 zeroext %b) safestack { +; CHECK-LABEL: define void @i +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -32 + %x1 = alloca i64, align 8 + %x2 = alloca i64, align 8 + %y = alloca i64, align 8 + %y1 = alloca i64, align 8 + %y2 = alloca i64, align 8 + %z = alloca i64, align 8 + %z1 = alloca i64, align 8 + %z2 = alloca i64, align 8 + %0 = bitcast i64* %x1 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %0) + %1 = bitcast i64* %x2 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %1) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %x1) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -16 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %x2) + br i1 %a, label %if.then, label %if.else4 + +if.then: ; preds = %entry + %2 = bitcast i64* %y to i8* + call void @llvm.lifetime.start(i64 -1, i8* %2) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -24 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %y) + br i1 %b, label %if.then3, label %if.else + +if.then3: ; preds = %if.then + %3 = bitcast i64* %y1 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %3) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -32 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %y1) + call void @llvm.lifetime.end(i64 -1, i8* %3) + br label %if.end + +if.else: ; preds = %if.then + %4 = bitcast i64* %y2 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %4) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -32 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %y2) + call void @llvm.lifetime.end(i64 -1, i8* %4) + br label %if.end + +if.end: ; preds = %if.else, %if.then3 + call void @llvm.lifetime.end(i64 -1, i8* %2) + br label %if.end9 + +if.else4: ; preds = %entry + %5 = bitcast i64* %z to i8* + call void @llvm.lifetime.start(i64 -1, i8* %5) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -24 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %z) + br i1 %b, label %if.then6, label %if.else7 + +if.then6: ; preds = %if.else4 + %6 = bitcast i64* %z1 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %6) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -32 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %z1) + call void @llvm.lifetime.end(i64 -1, i8* %6) + br label %if.end8 + +if.else7: ; preds = %if.else4 + %7 = bitcast i64* %z2 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %7) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -32 +; CHECK: call void @capture64( + call void @capture64(i64* nonnull %z2) + call void @llvm.lifetime.end(i64 -1, i8* %7) + br label %if.end8 + +if.end8: ; preds = %if.else7, %if.then6 + call void @llvm.lifetime.end(i64 -1, i8* %5) + br label %if.end9 + +if.end9: ; preds = %if.end8, %if.end + call void @llvm.lifetime.end(i64 -1, i8* %1) + call void @llvm.lifetime.end(i64 -1, i8* %0) + ret void +} + +; lifetime for x ends in 2 different BBs +define void @no_merge1(i1 %d) safestack { +; CHECK-LABEL: define void @no_merge1( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -16 + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + call void @llvm.lifetime.start(i64 -1, i8* %x0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %x) + br i1 %d, label %bb2, label %bb3 +bb2: + call void @llvm.lifetime.start(i64 -1, i8* %y0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 +; CHECK: call void @capture32( + call void @capture32(i32* %y) + call void @llvm.lifetime.end(i64 -1, i8* %y0) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + ret void +bb3: + call void @llvm.lifetime.end(i64 -1, i8* %x0) + ret void +} + +define void @merge1(i1 %d) safestack { +; CHECK-LABEL: define void @merge1( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -16 + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + call void @llvm.lifetime.start(i64 -1, i8* %x0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %x) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + br i1 %d, label %bb2, label %bb3 +bb2: + call void @llvm.lifetime.start(i64 -1, i8* %y0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %y) + call void @llvm.lifetime.end(i64 -1, i8* %y0) + ret void +bb3: + ret void +} + +; Missing lifetime.end +define void @merge2_noend(i1 %d) safestack { +; CHECK-LABEL: define void @merge2_noend( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -16 + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + call void @llvm.lifetime.start(i64 -1, i8* %x0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %x) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + br i1 %d, label %bb2, label %bb3 +bb2: + call void @llvm.lifetime.start(i64 -1, i8* %y0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %y) + ret void +bb3: + ret void +} + +; Missing lifetime.end +define void @merge3_noend(i1 %d) safestack { +; CHECK-LABEL: define void @merge3_noend( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -16 + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* + call void @llvm.lifetime.start(i64 -1, i8* %x0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %x) + br i1 %d, label %bb2, label %bb3 +bb2: + call void @llvm.lifetime.end(i64 -1, i8* %x0) + call void @llvm.lifetime.start(i64 -1, i8* %y0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %y) + ret void +bb3: + ret void +} + +; Missing lifetime.start +define void @nomerge4_nostart(i1 %d) safestack { +; CHECK-LABEL: define void @nomerge4_nostart( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -16 + %x = alloca i32, align 4 + %y = alloca i32, align 4 + %x0 = bitcast i32* %x to i8* + %y0 = bitcast i32* %y to i8* +; CHECK: getelementptr i8, i8* %[[USP]], i32 -4 +; CHECK: call void @capture32( + call void @capture32(i32* %x) + call void @llvm.lifetime.end(i64 -1, i8* %x0) + br i1 %d, label %bb2, label %bb3 +bb2: + call void @llvm.lifetime.start(i64 -1, i8* %y0) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -8 +; CHECK: call void @capture32( + call void @capture32(i32* %y) + ret void +bb3: + ret void +} + +define void @array_merge() safestack { +; CHECK-LABEL: define void @array_merge( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -800 + %A.i1 = alloca [100 x i32], align 4 + %B.i2 = alloca [100 x i32], align 4 + %A.i = alloca [100 x i32], align 4 + %B.i = alloca [100 x i32], align 4 + %0 = bitcast [100 x i32]* %A.i to i8* + call void @llvm.lifetime.start(i64 -1, i8* %0) + %1 = bitcast [100 x i32]* %B.i to i8* + call void @llvm.lifetime.start(i64 -1, i8* %1) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -400 +; CHECK: call void @capture100x32( + call void @capture100x32([100 x i32]* %A.i) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -800 +; CHECK: call void @capture100x32( + call void @capture100x32([100 x i32]* %B.i) + call void @llvm.lifetime.end(i64 -1, i8* %0) + call void @llvm.lifetime.end(i64 -1, i8* %1) + %2 = bitcast [100 x i32]* %A.i1 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %2) + %3 = bitcast [100 x i32]* %B.i2 to i8* + call void @llvm.lifetime.start(i64 -1, i8* %3) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -400 +; CHECK: call void @capture100x32( + call void @capture100x32([100 x i32]* %A.i1) +; CHECK: getelementptr i8, i8* %[[USP]], i32 -800 +; CHECK: call void @capture100x32( + call void @capture100x32([100 x i32]* %B.i2) + call void @llvm.lifetime.end(i64 -1, i8* %2) + call void @llvm.lifetime.end(i64 -1, i8* %3) + ret void +} + +define void @myCall_pr15707() safestack { +; CHECK-LABEL: define void @myCall_pr15707( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -200000 + %buf1 = alloca i8, i32 100000, align 16 + %buf2 = alloca i8, i32 100000, align 16 + + call void @llvm.lifetime.start(i64 -1, i8* %buf1) + call void @llvm.lifetime.end(i64 -1, i8* %buf1) + + call void @llvm.lifetime.start(i64 -1, i8* %buf1) + call void @llvm.lifetime.start(i64 -1, i8* %buf2) + call void @capture8(i8* %buf1) + call void @capture8(i8* %buf2) + ret void +} + +; Check that we don't assert and crash even when there are allocas +; outside the declared lifetime regions. +define void @bad_range() safestack { +; CHECK-LABEL: define void @bad_range( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; A.i and B.i unsafe, not merged +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -800 +; A.i1 and B.i2 safe +; CHECK: = alloca [100 x i32], align 4 +; CHECK: = alloca [100 x i32], align 4 + + %A.i1 = alloca [100 x i32], align 4 + %B.i2 = alloca [100 x i32], align 4 + %A.i = alloca [100 x i32], align 4 + %B.i = alloca [100 x i32], align 4 + %0 = bitcast [100 x i32]* %A.i to i8* + call void @llvm.lifetime.start(i64 -1, i8* %0) nounwind + %1 = bitcast [100 x i32]* %B.i to i8* + call void @llvm.lifetime.start(i64 -1, i8* %1) nounwind + call void @capture100x32([100 x i32]* %A.i) + call void @capture100x32([100 x i32]* %B.i) + call void @llvm.lifetime.end(i64 -1, i8* %0) nounwind + call void @llvm.lifetime.end(i64 -1, i8* %1) nounwind + br label %block2 + +block2: + ; I am used outside the marked lifetime. + call void @capture100x32([100 x i32]* %A.i) + call void @capture100x32([100 x i32]* %B.i) + ret void +} + +%struct.Klass = type { i32, i32 } + +define i32 @shady_range(i32 %argc, i8** nocapture %argv) safestack { +; CHECK-LABEL: define i32 @shady_range( +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr +; CHECK-NEXT: getelementptr i8, i8* %[[USP]], i32 -64 + %a.i = alloca [4 x %struct.Klass], align 16 + %b.i = alloca [4 x %struct.Klass], align 16 + %a8 = bitcast [4 x %struct.Klass]* %a.i to i8* + %b8 = bitcast [4 x %struct.Klass]* %b.i to i8* + ; I am used outside the lifetime zone below: + %z2 = getelementptr inbounds [4 x %struct.Klass], [4 x %struct.Klass]* %a.i, i64 0, i64 0, i32 0 + call void @llvm.lifetime.start(i64 -1, i8* %a8) + call void @llvm.lifetime.start(i64 -1, i8* %b8) + call void @capture8(i8* %a8) + call void @capture8(i8* %b8) + %z3 = load i32, i32* %z2, align 16 + call void @llvm.lifetime.end(i64 -1, i8* %a8) + call void @llvm.lifetime.end(i64 -1, i8* %b8) + ret i32 %z3 +} + +declare void @llvm.lifetime.start(i64, i8* nocapture) +declare void @llvm.lifetime.end(i64, i8* nocapture) +declare void @capture8(i8*) +declare void @capture32(i32*) +declare void @capture64(i64*) +declare void @capture100x32([100 x i32]*) |