; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple x86_64-apple-macosx10.13.0 < %s | FileCheck %s --check-prefix=X86_64 ; RUN: llc -mtriple i386-apple-macosx10.13.0 < %s | FileCheck %s --check-prefix=X86 ; The MacOS tripples are used to get trapping behavior on the "unreachable" IR ; instruction, so that the placement of the ud2 instruction could be verified. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The IR was created using the following C code: ;; typedef void *jmp_buf; ;; jmp_buf buf; ;; ;; __attribute__((noinline)) int bar(int i) { ;; int j = i - 111; ;; __builtin_longjmp(&buf, 1); ;; return j; ;; } ;; ;; int foo(int i) { ;; int j = i * 11; ;; if (!__builtin_setjmp(&buf)) { ;; j += 33 + bar(j); ;; } ;; return j + i; ;; } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @buf = common local_unnamed_addr global i8* null, align 8 ; Functions that use LongJmp should fix the Shadow Stack using previosuly saved ; ShadowStackPointer in the input buffer. ; The fix requires unwinding the shadow stack to the last SSP. define i32 @bar(i32 %i) local_unnamed_addr { ; X86_64-LABEL: bar: ; X86_64: ## %bb.0: ## %entry ; X86_64-NEXT: pushq %rbp ; X86_64-NEXT: .cfi_def_cfa_offset 16 ; X86_64-NEXT: .cfi_offset %rbp, -16 ; X86_64-NEXT: movq _buf@{{.*}}(%rip), %rax ; X86_64-NEXT: movq (%rax), %rax ; X86_64-NEXT: xorq %rdx, %rdx ; X86_64-NEXT: rdsspq %rdx ; X86_64-NEXT: testq %rdx, %rdx ; X86_64-NEXT: je LBB0_5 ; X86_64-NEXT: ## %bb.1: ## %entry ; X86_64-NEXT: movq 24(%rax), %rcx ; X86_64-NEXT: subq %rdx, %rcx ; X86_64-NEXT: jbe LBB0_5 ; X86_64-NEXT: ## %bb.2: ## %entry ; X86_64-NEXT: shrq $3, %rcx ; X86_64-NEXT: incsspq %rcx ; X86_64-NEXT: shrq $8, %rcx ; X86_64-NEXT: je LBB0_5 ; X86_64-NEXT: ## %bb.3: ## %entry ; X86_64-NEXT: shlq %rcx ; X86_64-NEXT: movq $128, %rdx ; X86_64-NEXT: LBB0_4: ## %entry ; X86_64-NEXT: ## =>This Inner Loop Header: Depth=1 ; X86_64-NEXT: incsspq %rdx ; X86_64-NEXT: decq %rcx ; X86_64-NEXT: jne LBB0_4 ; X86_64-NEXT: LBB0_5: ## %entry ; X86_64-NEXT: movq (%rax), %rbp ; X86_64-NEXT: movq 8(%rax), %rcx ; X86_64-NEXT: movq 16(%rax), %rsp ; X86_64-NEXT: jmpq *%rcx ; ; X86-LABEL: bar: ; X86: ## %bb.0: ## %entry ; X86-NEXT: pushl %ebp ; X86-NEXT: .cfi_def_cfa_offset 8 ; X86-NEXT: .cfi_offset %ebp, -8 ; X86-NEXT: movl L_buf$non_lazy_ptr, %eax ; X86-NEXT: movl (%eax), %eax ; X86-NEXT: xorl %edx, %edx ; X86-NEXT: rdsspd %edx ; X86-NEXT: testl %edx, %edx ; X86-NEXT: je LBB0_5 ; X86-NEXT: ## %bb.1: ## %entry ; X86-NEXT: movl 12(%eax), %ecx ; X86-NEXT: subl %edx, %ecx ; X86-NEXT: jbe LBB0_5 ; X86-NEXT: ## %bb.2: ## %entry ; X86-NEXT: shrl $2, %ecx ; X86-NEXT: incsspd %ecx ; X86-NEXT: shrl $8, %ecx ; X86-NEXT: je LBB0_5 ; X86-NEXT: ## %bb.3: ## %entry ; X86-NEXT: shll %ecx ; X86-NEXT: movl $128, %edx ; X86-NEXT: LBB0_4: ## %entry ; X86-NEXT: ## =>This Inner Loop Header: Depth=1 ; X86-NEXT: incsspd %edx ; X86-NEXT: decl %ecx ; X86-NEXT: jne LBB0_4 ; X86-NEXT: LBB0_5: ## %entry ; X86-NEXT: movl (%eax), %ebp ; X86-NEXT: movl 4(%eax), %ecx ; X86-NEXT: movl 8(%eax), %esp ; X86-NEXT: jmpl *%ecx entry: %0 = load i8*, i8** @buf, align 8 tail call void @llvm.eh.sjlj.longjmp(i8* %0) unreachable } declare void @llvm.eh.sjlj.longjmp(i8*) ; Functions that call SetJmp should save the current ShadowStackPointer for ; future fixing of the Shadow Stack. define i32 @foo(i32 %i) local_unnamed_addr { ; X86_64-LABEL: foo: ; X86_64: ## %bb.0: ## %entry ; X86_64-NEXT: pushq %rbp ; X86_64-NEXT: .cfi_def_cfa_offset 16 ; X86_64-NEXT: .cfi_offset %rbp, -16 ; X86_64-NEXT: movq %rsp, %rbp ; X86_64-NEXT: .cfi_def_cfa_register %rbp ; X86_64-NEXT: pushq %r15 ; X86_64-NEXT: pushq %r14 ; X86_64-NEXT: pushq %r13 ; X86_64-NEXT: pushq %r12 ; X86_64-NEXT: pushq %rbx ; X86_64-NEXT: pushq %rax ; X86_64-NEXT: .cfi_offset %rbx, -56 ; X86_64-NEXT: .cfi_offset %r12, -48 ; X86_64-NEXT: .cfi_offset %r13, -40 ; X86_64-NEXT: .cfi_offset %r14, -32 ; X86_64-NEXT: .cfi_offset %r15, -24 ; X86_64-NEXT: ## kill: def $edi killed $edi def $rdi ; X86_64-NEXT: movq %rdi, {{[-0-9]+}}(%r{{[sb]}}p) ## 8-byte Spill ; X86_64-NEXT: movq _buf@{{.*}}(%rip), %rax ; X86_64-NEXT: movq (%rax), %rax ; X86_64-NEXT: movq %rbp, (%rax) ; X86_64-NEXT: movq %rsp, 16(%rax) ; X86_64-NEXT: leaq {{.*}}(%rip), %rcx ; X86_64-NEXT: movq %rcx, 8(%rax) ; X86_64-NEXT: xorq %rcx, %rcx ; X86_64-NEXT: rdsspq %rcx ; X86_64-NEXT: movq %rcx, 24(%rax) ; X86_64-NEXT: #EH_SjLj_Setup LBB1_4 ; X86_64-NEXT: ## %bb.1: ## %entry ; X86_64-NEXT: xorl %eax, %eax ; X86_64-NEXT: testl %eax, %eax ; X86_64-NEXT: jne LBB1_3 ; X86_64-NEXT: jmp LBB1_5 ; X86_64-NEXT: LBB1_4: ## Block address taken ; X86_64-NEXT: ## %entry ; X86_64-NEXT: movl $1, %eax ; X86_64-NEXT: testl %eax, %eax ; X86_64-NEXT: je LBB1_5 ; X86_64-NEXT: LBB1_3: ## %if.end ; X86_64-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rax ## 8-byte Reload ; X86_64-NEXT: shll $2, %eax ; X86_64-NEXT: leal (%rax,%rax,2), %eax ; X86_64-NEXT: addq $8, %rsp ; X86_64-NEXT: popq %rbx ; X86_64-NEXT: popq %r12 ; X86_64-NEXT: popq %r13 ; X86_64-NEXT: popq %r14 ; X86_64-NEXT: popq %r15 ; X86_64-NEXT: popq %rbp ; X86_64-NEXT: retq ; X86_64-NEXT: LBB1_5: ## %if.then ; X86_64-NEXT: callq _bar ; X86_64-NEXT: ud2 ; ; X86-LABEL: foo: ; X86: ## %bb.0: ## %entry ; X86-NEXT: pushl %ebp ; X86-NEXT: .cfi_def_cfa_offset 8 ; X86-NEXT: .cfi_offset %ebp, -8 ; X86-NEXT: movl %esp, %ebp ; X86-NEXT: .cfi_def_cfa_register %ebp ; X86-NEXT: pushl %ebx ; X86-NEXT: pushl %edi ; X86-NEXT: pushl %esi ; X86-NEXT: subl $12, %esp ; X86-NEXT: .cfi_offset %esi, -20 ; X86-NEXT: .cfi_offset %edi, -16 ; X86-NEXT: .cfi_offset %ebx, -12 ; X86-NEXT: movl L_buf$non_lazy_ptr, %eax ; X86-NEXT: movl (%eax), %eax ; X86-NEXT: movl %ebp, (%eax) ; X86-NEXT: movl %esp, 16(%eax) ; X86-NEXT: movl $LBB1_4, 4(%eax) ; X86-NEXT: xorl %ecx, %ecx ; X86-NEXT: rdsspd %ecx ; X86-NEXT: movl %ecx, 12(%eax) ; X86-NEXT: #EH_SjLj_Setup LBB1_4 ; X86-NEXT: ## %bb.1: ## %entry ; X86-NEXT: xorl %eax, %eax ; X86-NEXT: testl %eax, %eax ; X86-NEXT: jne LBB1_3 ; X86-NEXT: jmp LBB1_5 ; X86-NEXT: LBB1_4: ## Block address taken ; X86-NEXT: ## %entry ; X86-NEXT: movl $1, %eax ; X86-NEXT: testl %eax, %eax ; X86-NEXT: je LBB1_5 ; X86-NEXT: LBB1_3: ## %if.end ; X86-NEXT: movl 8(%ebp), %eax ; X86-NEXT: shll $2, %eax ; X86-NEXT: leal (%eax,%eax,2), %eax ; X86-NEXT: addl $12, %esp ; X86-NEXT: popl %esi ; X86-NEXT: popl %edi ; X86-NEXT: popl %ebx ; X86-NEXT: popl %ebp ; X86-NEXT: retl ; X86-NEXT: LBB1_5: ## %if.then ; X86-NEXT: calll _bar ; X86-NEXT: ud2 entry: %0 = load i8*, i8** @buf, align 8 %1 = bitcast i8* %0 to i8** %2 = tail call i8* @llvm.frameaddress(i32 0) store i8* %2, i8** %1, align 8 %3 = tail call i8* @llvm.stacksave() %4 = getelementptr inbounds i8, i8* %0, i64 16 %5 = bitcast i8* %4 to i8** store i8* %3, i8** %5, align 8 %6 = tail call i32 @llvm.eh.sjlj.setjmp(i8* %0) %tobool = icmp eq i32 %6, 0 br i1 %tobool, label %if.then, label %if.end if.then: ; preds = %entry %call = tail call i32 @bar(i32 undef) unreachable if.end: ; preds = %entry %add2 = mul nsw i32 %i, 12 ret i32 %add2 } declare i8* @llvm.frameaddress(i32) declare i8* @llvm.stacksave() declare i32 @llvm.eh.sjlj.setjmp(i8*) !llvm.module.flags = !{!0} !0 = !{i32 4, !"cf-protection-return", i32 1}