Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ add_llvm_component_library(LLVMCodeGen
WindowsSecureHotPatching.cpp
WinEHPrepare.cpp
WinEHRewrite.cpp
WinEHX86State.cpp
XRayInstrumentation.cpp
${GeneratedMLSources}

Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/WinEHPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/WinEHPrepare.h"
#include "WinEHX86State.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -672,6 +673,7 @@ void llvm::calculateSEHStateNumbers(const Function *Fn,
::calculateSEHStateNumbers(FuncInfo, FirstNonPHI, -1);
}

orderWinX86SEHRewriteStates(*Fn, FuncInfo);
calculateStateNumbersForInvokes(Fn, FuncInfo);

bool IsEHa = Fn->getParent()->getModuleFlag("eh-asynch");
Expand Down
76 changes: 76 additions & 0 deletions llvm/lib/CodeGen/WinEHX86State.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//===- WinEHX86State.cpp - Rewrite-owned x86 SEH states -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "WinEHX86State.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/TargetParser/Triple.h"
#include <numeric>

using namespace llvm;

void llvm::orderWinX86SEHRewriteStates(const Function &F, WinEHFuncInfo &Info) {
const Triple &Target = F.getParent()->getTargetTriple();
if (!F.hasFnAttribute(mc_rewrite::RewriteWinX86RegistrationStateAttribute) ||
Target.getArch() != Triple::x86 || !Target.isWindowsMSVCEnvironment() ||
Info.SEHUnwindMap.empty())
return;

// The CRT stops local unwind when the current numeric state is at or below
// the saved state. A depth-first ordering preserves enclosing edges but can
// reverse two sibling regions, changing setjmp/longjmp behavior. Complete
// source scope identities also specify the ordering of those siblings.
// Unannotated frontend graphs retain their normal numbering.
if (llvm::any_of(Info.SEHUnwindMap,
[](const auto &Entry) { return !Entry.RewriteSemantic; }))
return;
SmallVector<unsigned, 4> Order(Info.SEHUnwindMap.size());
std::iota(Order.begin(), Order.end(), 0);
for (const auto &Entry : Info.SEHUnwindMap) {
const auto &Token = *Entry.RewriteSemantic;
if (Token.Kind != mc_rewrite::RewriteWinEHSemanticKind::SEHScope ||
Token.Clause)
report_fatal_error("invalid x86 SEH rewrite state identity");
}
auto SourceState = [&](unsigned Old) {
return Info.SEHUnwindMap[Old].RewriteSemantic->Region;
};
llvm::sort(Order, [&](unsigned A, unsigned B) {
return SourceState(A) < SourceState(B);
});
SmallVector<int, 4> Remap(Order.size());
for (unsigned New = 0; New != Order.size(); ++New) {
if (New && SourceState(Order[New - 1]) == SourceState(Order[New]))
report_fatal_error("duplicate x86 SEH rewrite state identity");
Remap[Order[New]] = New;
}
auto MapState = [&](int State) {
if (State < 0)
return State;
if (unsigned(State) >= Remap.size())
report_fatal_error("invalid x86 SEH rewrite state reference");
return Remap[State];
};
SmallVector<SEHUnwindMapEntry, 4> Ordered;
for (unsigned Old : Order) {
auto Entry = Info.SEHUnwindMap[Old];
Entry.ToState = MapState(Entry.ToState);
if (Entry.ToState >= int(Ordered.size()))
report_fatal_error(
"x86 SEH rewrite enclosing state must precede its scope");
Ordered.push_back(std::move(Entry));
}
for (auto &[Pad, State] : Info.EHPadStateMap)
State = MapState(State);
for (auto &[Pad, State] : Info.FuncletBaseStateMap)
State = MapState(State);
Info.SEHUnwindMap = std::move(Ordered);
}
20 changes: 20 additions & 0 deletions llvm/lib/CodeGen/WinEHX86State.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===- WinEHX86State.h - Rewrite-owned x86 SEH states -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_CODEGEN_WINEHX86STATE_H
#define LLVM_LIB_CODEGEN_WINEHX86STATE_H

namespace llvm {
class Function;
struct WinEHFuncInfo;

/// Preserve source state comparisons before assigning invoke and async states.
void orderWinX86SEHRewriteStates(const Function &F, WinEHFuncInfo &Info);
} // namespace llvm

#endif
95 changes: 95 additions & 0 deletions llvm/test/CodeGen/X86/win32-seh-source-state-order.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
; RUN: llc -O0 < %s | FileCheck %s
; RUN: llc -O2 < %s | FileCheck %s
; RUN: opt -passes='default<O2>' -S < %s | llc -O2 | FileCheck %s
; RUN: llc -O2 -filetype=obj < %s -o %t.obj
; RUN: sed 's/i8 1, i32 1, i32 0/i8 1, i32 0, i32 0/' %s | not --crash llc -filetype=obj -o /dev/null 2>&1 | FileCheck %s --check-prefix=DUPLICATE
; RUN: sed 's/i8 1, i32 0, i32 0/i8 1, i32 9, i32 0/' %s | not --crash llc -filetype=obj -o /dev/null 2>&1 | FileCheck %s --check-prefix=OUTER
;
; DUPLICATE: duplicate x86 SEH rewrite state identity
; OUTER: x86 SEH rewrite enclosing state must precede its scope
;
; The CRT compares the live state with the saved numeric setjmp bound. The
; predecessor traversal used to number this source graph 0,3,4,1,2, causing
; longjmp from scope 4 to scope 1 to skip the finally entirely.

target datalayout = "e-m:x-p:32:32-i64:64-n8:16:32-S32"
target triple = "i686-pc-windows-msvc"

@llvm.compiler.used = appending global [2 x ptr] [ptr @filter, ptr @cleanup], section "llvm.metadata"

; CHECK-LABEL: _parent:
; CHECK: calll __setjmp3
; CHECK-DAG: Lparent$seh_state$0$1$2$3$4 = 0
; CHECK-DAG: Lparent$seh_state$1$5$6$7$8 = 1
; CHECK-DAG: Lparent$seh_state$2$9$A$B$C = 2
; CHECK-DAG: Lparent$seh_state$3$D$E$F$10 = 3
; CHECK-DAG: Lparent$seh_state$4$11$12$13$14 = 4

define void @parent(ptr %buffer) #0 personality ptr @_except_handler3 {
entry:
invoke void @may_throw(ptr null) to label %capture unwind label %scope2
capture:
%saved = invoke i32 (ptr, i32, ...) @_setjmp3(ptr %buffer, i32 0)
to label %active unwind label %scope1
active:
invoke void @may_throw(ptr %buffer) to label %done unwind label %finally4
scope0:
%switch0 = catchswitch within none [label %handler0] unwind to caller
handler0:
%pad0 = catchpad within %switch0 [ptr @filter], !llvm.rewrite.windows-eh.semantic !0
catchret from %pad0 to label %done
scope1:
%switch1 = catchswitch within none [label %handler1] unwind label %scope0
handler1:
%pad1 = catchpad within %switch1 [ptr @filter], !llvm.rewrite.windows-eh.semantic !1
catchret from %pad1 to label %done
scope2:
%switch2 = catchswitch within none [label %handler2] unwind label %scope1
handler2:
%pad2 = catchpad within %switch2 [ptr @filter], !llvm.rewrite.windows-eh.semantic !2
catchret from %pad2 to label %done
scope3:
%switch3 = catchswitch within none [label %handler3] unwind label %scope0
handler3:
%pad3 = catchpad within %switch3 [ptr @filter], !llvm.rewrite.windows-eh.semantic !3
catchret from %pad3 to label %done
finally4:
%pad4 = cleanuppad within none [], !llvm.rewrite.windows-eh.semantic !4
%frame = call ptr @llvm.localaddress()
call void @cleanup(i8 1, ptr %frame) [ "funclet"(token %pad4) ]
cleanupret from %pad4 unwind label %scope3
done:
ret void
}

define internal i32 @filter() #1 {
entry:
%runtime = call ptr @llvm.frameaddress.p0(i32 1)
%frame = call ptr @llvm.eh.recoverfp(ptr @parent, ptr %runtime)
call void @llvm.x86.seh.setstate(ptr @parent, ptr %frame, i32 2, i64 9, i64 10, i64 11, i64 12, i64 1)
ret i32 -1
}

define internal void @cleanup(i8 %abnormal, ptr %frame) #1 {
entry:
call void @llvm.x86.seh.setstate(ptr @parent, ptr %frame, i32 3, i64 13, i64 14, i64 15, i64 16, i64 2)
call void @may_throw(ptr %frame)
ret void
}

declare void @may_throw(ptr)
declare i32 @_except_handler3(...)
declare i32 @_setjmp3(ptr, i32, ...) returns_twice
declare ptr @llvm.localaddress()
declare ptr @llvm.frameaddress.p0(i32 immarg)
declare ptr @llvm.eh.recoverfp(ptr, ptr)
declare void @llvm.x86.seh.setstate(ptr, ptr, i32 immarg, i64 immarg, i64 immarg, i64 immarg, i64 immarg, i64 immarg)

attributes #0 = { noinline "frame-pointer"="all" "llvm.rewrite.win-x86-registration-state" }
attributes #1 = { noinline "frame-pointer"="all" }

!0 = !{i32 1, i8 1, i32 0, i32 0, i64 1, i64 2, i64 3, i64 4}
!1 = !{i32 1, i8 1, i32 1, i32 0, i64 5, i64 6, i64 7, i64 8}
!2 = !{i32 1, i8 1, i32 2, i32 0, i64 9, i64 10, i64 11, i64 12}
!3 = !{i32 1, i8 1, i32 3, i32 0, i64 13, i64 14, i64 15, i64 16}
!4 = !{i32 1, i8 1, i32 4, i32 0, i64 17, i64 18, i64 19, i64 20}