diff --git a/llvm/lib/CodeGen/StackColoring.cpp b/llvm/lib/CodeGen/StackColoring.cpp --- a/llvm/lib/CodeGen/StackColoring.cpp +++ b/llvm/lib/CodeGen/StackColoring.cpp @@ -651,6 +651,7 @@ } // Walk the instructions in the block to look for start/end ops. + bool FirstLoad = true; for (MachineInstr &MI : *MBB) { if (MI.getOpcode() == TargetOpcode::LIFETIME_START || MI.getOpcode() == TargetOpcode::LIFETIME_END) { @@ -687,6 +688,14 @@ if (! BetweenStartEnd.test(Slot)) { ConservativeSlots.set(Slot); } + // For variable X in catch(X), we put &&X into ConservativeSlots + // to prevent using LifetimeStartOnFirstUse. Because &&X may merged + // with class object which may call destructor after write &&X. The + // loader of &&X is the first LOAD MI in EHPad. + if (MBB->isEHPad() && MI.mayLoad() && FirstLoad) { + FirstLoad = false; + ConservativeSlots.set(Slot); + } } } } diff --git a/llvm/test/CodeGen/X86/StackColoring-first-use-in-catch.mir b/llvm/test/CodeGen/X86/StackColoring-first-use-in-catch.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/StackColoring-first-use-in-catch.mir @@ -0,0 +1,339 @@ +# RUN: llc -mtriple="i386-pc-windows-msvc" -run-pass=stack-coloring %s -o - | FileCheck %s + +## try{ +## T2 ex2; +## ... +## call func_bar; +## ... +## }catch (T1 ex){ +## ... +## } +## The Exception Handle lib may write the catch value and return the point (T1**) +## back, Here ex2's destructor may do some work after writing &&ex (T1**), it +## may overwite the &&ex's value. +## This test tests is preventing merging stack.1.ex2 and %stack.2.ex successful +## or not (stack.2.ex is the &&ex) + +## If only ex or ex2 in stack list, it is failed to prevent merging. +# CHECK: stack: +# CHECK: id: 1, name: ex2, type: default, offset: 0, size: 4, alignment: 4, +# CHECK: id: 2, name: ex, type: default, offset: 0, size: 4, alignment: 4, + +## And if fail to prevent merging, the CHECK at line 33 will be somelike +## "%7:gr32 = MOV32rm %stack.1.ex2 ..." +# CHECK: bb.0.entry: +# CHECK: %6:gr32 = LEA32r %stack.1.ex2, 1, $noreg, 0, $noreg +# CHECK: MOV32mr %5, 1, $noreg, 0, $noreg, killed %6 :: (store 4 into stack) +# CHECK: CALLpcrel32 @"?bar@@YAXAAVcb@@@Z" +# CHECK: JMP_1 %bb.1 +# CHECK: bb.1.invoke.cont1: +# CHECK: %8:gr32 = MOV32r0 implicit-def dead $eflags +# CHECK: JMP_1 %bb.4 +# CHECK: bb.2.catch (landing-pad, ehfunclet-entry): +# CHECK: %7:gr32 = MOV32rm %stack.2.ex, 1, $noreg, 0, $noreg :: (dereferenceable load 4 from %ir.ex, !tbaa !14) +# CHECK: %0:gr32 = MOV32rm killed %7, 1, $noreg, 0, $noreg :: (load 4 from %ir.member2) +# CHECK: CATCHRET %bb.3, %bb.0 +# CHECK: bb.3.catch (landing-pad): +# CHECK: JMP_4 %bb.4 +# CHECK: bb.4.try.cont: + +--- | + ; ModuleID = 'ts.ll' + source_filename = "ts.cpp" + target datalayout = "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:32-n8:16:32-a:0:32-S32" + target triple = "i386-pc-windows-msvc" + + %rtti.TypeDescriptor8 = type { i8**, i8*, [9 x i8] } + %CXXExceptionRegistration = type { i8*, %EHRegistrationNode, i32 } + %EHRegistrationNode = type { %EHRegistrationNode*, i8* } + %class.cb = type { i32 } + + $"??_R0?AVcb@@@8" = comdat any + + @"??_7type_info@@6B@" = external constant i8* + @"??_R0?AVcb@@@8" = linkonce_odr global %rtti.TypeDescriptor8 { i8** @"??_7type_info@@6B@", i8* null, [9 x i8] c".?AVcb@@\00" }, comdat + + define dso_local i32 @"?run@@YAHXZ"() local_unnamed_addr #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { + entry: + %0 = alloca %CXXExceptionRegistration, align 4 + %1 = bitcast %CXXExceptionRegistration* %0 to i8* + call void @llvm.x86.seh.ehregnode(i8* %1) + %2 = call i8* @llvm.stacksave() + %3 = getelementptr inbounds %CXXExceptionRegistration, %CXXExceptionRegistration* %0, i32 0, i32 0 + store i8* %2, i8** %3, align 4 + %4 = getelementptr inbounds %CXXExceptionRegistration, %CXXExceptionRegistration* %0, i32 0, i32 2 + store i32 -1, i32* %4, align 4 + %5 = getelementptr inbounds %CXXExceptionRegistration, %CXXExceptionRegistration* %0, i32 0, i32 1 + %6 = getelementptr inbounds %EHRegistrationNode, %EHRegistrationNode* %5, i32 0, i32 1 + store i8* bitcast (i32 (i8*, i8*, i8*, i8*)* @"__ehhandler$?run@@YAHXZ" to i8*), i8** %6, align 4 + %7 = load %EHRegistrationNode*, %EHRegistrationNode* addrspace(257)* null, align 4 + %8 = getelementptr inbounds %EHRegistrationNode, %EHRegistrationNode* %5, i32 0, i32 0 + store %EHRegistrationNode* %7, %EHRegistrationNode** %8, align 4 + store %EHRegistrationNode* %5, %EHRegistrationNode* addrspace(257)* null, align 4 + %zx = alloca %class.cb, align 4 + %ex2 = alloca %class.cb, align 4 + %ex = alloca %class.cb*, align 4 + %9 = bitcast %class.cb* %ex2 to i8* + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %9) #4 + %member.i1 = bitcast %class.cb* %ex2 to i32* + store i32 2, i32* %member.i1, align 4, !tbaa !9 + %10 = getelementptr inbounds %CXXExceptionRegistration, %CXXExceptionRegistration* %0, i32 0, i32 2 + store i32 0, i32* %10, align 4 + invoke void @"?bar@@YAXAAVcb@@@Z"(%class.cb* nonnull align 4 dereferenceable(4) %ex2) + to label %invoke.cont1 unwind label %catch.dispatch + + invoke.cont1: ; preds = %entry + %11 = bitcast %class.cb* %ex2 to i8* + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %11) #4 + br label %try.cont + + catch.dispatch: ; preds = %entry + %12 = catchswitch within none [label %catch] unwind to caller + + catch: ; preds = %catch.dispatch + %13 = catchpad within %12 [%rtti.TypeDescriptor8* @"??_R0?AVcb@@@8", i32 8, %class.cb** %ex] + %14 = load %class.cb*, %class.cb** %ex, align 4, !tbaa !14 + %member2 = bitcast %class.cb* %14 to i32* + %15 = load i32, i32* %member2, align 4, !tbaa !9 + catchret from %13 to label %try.cont + + try.cont: ; preds = %catch, %invoke.cont1 + %x.0 = phi i32 [ 0, %invoke.cont1 ], [ %15, %catch ] + %16 = getelementptr inbounds %CXXExceptionRegistration, %CXXExceptionRegistration* %0, i32 0, i32 1 + %17 = getelementptr inbounds %EHRegistrationNode, %EHRegistrationNode* %16, i32 0, i32 0 + %18 = load %EHRegistrationNode*, %EHRegistrationNode** %17, align 4 + store %EHRegistrationNode* %18, %EHRegistrationNode* addrspace(257)* null, align 4 + ret i32 %x.0 + } + + ; Function Attrs: argmemonly nounwind willreturn + declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + + ; Function Attrs: nofree + declare dso_local i32 @__CxxFrameHandler3(...) #2 + + declare dso_local void @"?bar@@YAXAAVcb@@@Z"(%class.cb* nonnull align 4 dereferenceable(4)) local_unnamed_addr #3 + + ; Function Attrs: argmemonly nounwind willreturn + declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + + declare i32 @_setjmp3(i8*, i32, ...) + + ; Function Attrs: nounwind + declare i8* @llvm.stacksave() #4 + + define internal i32 @"__ehhandler$?run@@YAHXZ"(i8* %0, i8* %1, i8* %2, i8* %3) #5 { + entry: + %4 = call i8* @llvm.x86.seh.lsda(i8* bitcast (i32 ()* @"?run@@YAHXZ" to i8*)) + %5 = tail call i32 bitcast (i32 (...)* @__CxxFrameHandler3 to i32 (i8*, i8*, i8*, i8*, i8*)*)(i8* inreg %4, i8* %0, i8* %1, i8* %2, i8* %3) + ret i32 %5 + } + + ; Function Attrs: nounwind readnone + declare i8* @llvm.x86.seh.lsda(i8*) #6 + + declare x86_stdcallcc void @__CxxLongjmpUnwind(i8*) + + ; Function Attrs: nounwind + declare void @llvm.x86.seh.ehregnode(i8*) #4 + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) #4 + + attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #1 = { argmemonly nounwind willreturn } + attributes #2 = { nofree } + attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #4 = { nounwind } + attributes #5 = { "safeseh" } + attributes #6 = { nounwind readnone } + + !llvm.linker.options = !{!0, !1, !2, !3, !4, !5} + !llvm.module.flags = !{!6, !7} + !llvm.ident = !{!8} + + !0 = !{!"/DEFAULTLIB:libircmt.lib"} + !1 = !{!"/DEFAULTLIB:libmmd.lib"} + !2 = !{!"/DEFAULTLIB:msvcrt.lib"} + !3 = !{!"/DEFAULTLIB:oldnames.lib"} + !4 = !{!"/DEFAULTLIB:svml_dispmd.lib"} + !5 = !{!"/DEFAULTLIB:libdecimal.lib"} + !6 = !{i32 1, !"NumRegisterParameters", i32 0} + !7 = !{i32 1, !"wchar_size", i32 2} + !8 = !{!"Intel(R) oneAPI DPC++ Compiler 2021.1 (YYYY.x.0.MMDD)"} + !9 = !{!10, !11, i64 0} + !10 = !{!"struct@?AVcb@@", !11, i64 0} + !11 = !{!"int", !12, i64 0} + !12 = !{!"omnipotent char", !13, i64 0} + !13 = !{!"Simple C++ TBAA"} + !14 = !{!15, !15, i64 0} + !15 = !{!"any pointer", !12, i64 0} + +... +--- +name: '?run@@YAHXZ' +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr32, preferred-register: '' } + - { id: 4, class: gr32, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr32, preferred-register: '' } + - { id: 7, class: gr32, preferred-register: '' } + - { id: 8, class: gr32, preferred-register: '' } + - { id: 9, class: gr32, preferred-register: '' } +liveins: [] +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: false + hasCalls: true + stackProtector: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: true + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: + - { id: 0, name: 'zx', type: default, offset: 0, size: 16, + alignment: 4, stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: ex2, type: default, offset: 0, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: ex, type: default, offset: 0, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +callSites: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.0.entry: + successors: %bb.1(0x7ffff800), %bb.2(0x00000800) + + %2:gr32 = COPY $esp + MOV32mr %stack.0.zx, 1, $noreg, 0, $noreg, %2 :: (store 4 into %ir.3) + MOV32mi %stack.0.zx, 1, $noreg, 12, $noreg, -1 :: (store 4 into %ir.4) + %3:gr32 = nuw LEA32r %stack.0.zx, 1, $noreg, 4, $noreg + MOV32mi %stack.0.zx, 1, $noreg, 8, $noreg, @"__ehhandler$?run@@YAHXZ" :: (store 4 into %ir.6) + %4:gr32 = MOV32rm $noreg, 1, $noreg, 0, $fs :: (load 4 from `%EHRegistrationNode* addrspace(257)* null`, addrspace 257) + MOV32mr %stack.0.zx, 1, $noreg, 4, $noreg, killed %4 :: (store 4 into %ir.8) + MOV32mr $noreg, 1, $noreg, 0, $fs, killed %3 :: (store 4 into `%EHRegistrationNode* addrspace(257)* null`, addrspace 257) + LIFETIME_START %stack.2.ex + LIFETIME_START %stack.1.ex2 + MOV32mi %stack.1.ex2, 1, $noreg, 0, $noreg, 2 :: (store 4 into %ir.member.i1, !tbaa !9) + MOV32mi %stack.0.zx, 1, $noreg, 12, $noreg, 0 :: (store 4 into %ir.10) + ADJCALLSTACKDOWN32 4, 0, 0, implicit-def dead $esp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $esp, implicit $ssp + %5:gr32 = COPY $esp + %6:gr32 = LEA32r %stack.1.ex2, 1, $noreg, 0, $noreg + MOV32mr %5, 1, $noreg, 0, $noreg, killed %6 :: (store 4 into stack) + CALLpcrel32 @"?bar@@YAXAAVcb@@@Z", csr_noregs, implicit $esp, implicit $ssp, implicit-def $esp, implicit-def $ssp + ADJCALLSTACKUP32 4, 0, implicit-def dead $esp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $esp, implicit $ssp + LIFETIME_END %stack.1.ex2 + JMP_1 %bb.1 + + bb.1.invoke.cont1: + successors: %bb.3(0x80000000) + + %8:gr32 = MOV32r0 implicit-def dead $eflags + JMP_1 %bb.3 + + bb.2.catch (landing-pad, ehfunclet-entry): + successors: %bb.4(0x80000000) + + %7:gr32 = MOV32rm %stack.2.ex, 1, $noreg, 0, $noreg :: (dereferenceable load 4 from %ir.ex, !tbaa !14) + %0:gr32 = MOV32rm killed %7, 1, $noreg, 0, $noreg :: (load 4 from %ir.member2, !tbaa !9) + CATCHRET %bb.4, %bb.0 + + bb.4.catch (landing-pad): + successors: %bb.3(0x80000000) + + JMP_4 %bb.3 + + bb.3.try.cont: + %1:gr32 = PHI %0, %bb.4, %8, %bb.1 + %9:gr32 = MOV32rm %stack.0.zx, 1, $noreg, 4, $noreg :: (dereferenceable load 4 from %ir.17) + MOV32mr $noreg, 1, $noreg, 0, $fs, killed %9 :: (store 4 into `%EHRegistrationNode* addrspace(257)* null`, addrspace 257) + $eax = COPY %1 + LIFETIME_END %stack.2.ex + RET 0, $eax + +... +--- +name: '__ehhandler$?run@@YAHXZ' +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +registers: + - { id: 0, class: gr32, preferred-register: '' } +liveins: [] +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: + - { id: 0, type: default, offset: 12, size: 4, alignment: 4, stack-id: default, + isImmutable: false, isAliased: false, callee-saved-register: '', + callee-saved-restored: true, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 1, type: default, offset: 8, size: 4, alignment: 4, stack-id: default, + isImmutable: false, isAliased: false, callee-saved-register: '', + callee-saved-restored: true, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 2, type: default, offset: 4, size: 4, alignment: 4, stack-id: default, + isImmutable: false, isAliased: false, callee-saved-register: '', + callee-saved-restored: true, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 3, type: default, offset: 0, size: 4, alignment: 4, stack-id: default, + isImmutable: false, isAliased: false, callee-saved-register: '', + callee-saved-restored: true, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } +stack: [] +callSites: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.0.entry: + %0:gr32 = MOV32ri + $eax = COPY %0 + TCRETURNdi @__CxxFrameHandler3, 0, csr_32, implicit $esp, implicit $ssp, implicit $eax + +...