-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathast_catch_throw_fastinterp.cpp
More file actions
148 lines (129 loc) · 7.2 KB
/
ast_catch_throw_fastinterp.cpp
File metadata and controls
148 lines (129 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "ast_catch_throw.h"
#include "fastinterp_ast_helper.hpp"
#include "exception_helper.h"
#include "codegen_context.hpp"
namespace PochiVM
{
void AstThrowStmt::FastInterpSetupSpillLocation()
{
thread_pochiVMContext->m_fastInterpStackFrameManager->AssertNoTemp();
if (m_isLValueObject)
{
// The address of the LValue object is the second parameter to the copy-constructor callExpr
// We will only evaluate 'expr', not the 'callExpr'
// This is really poorly designed, but just leave it as it is for now.
//
AstCallExpr* callExpr = assert_cast<AstCallExpr*>(m_operand);
AstNodeBase* expr = callExpr->GetParams()[1];
expr->FastInterpSetupSpillLocation();
}
else
{
m_operand->FastInterpSetupSpillLocation();
}
}
FastInterpSnippet WARN_UNUSED AstThrowStmt::PrepareForFastInterp(FISpillLocation TESTBUILD_ONLY(spillLoc))
{
TestAssert(spillLoc.IsNoSpill());
thread_pochiVMContext->m_fastInterpStackFrameManager->AssertNoTemp();
// Important to declare the variable before the AutoScope: it must outlive the AutoScope
//
AstVariable variable(m_exceptionTypeId.AddPointer(), thread_llvmContext->m_curFunction, 1000000000 /*varSuffix*/);
// Create a new scope, which will contain the exception variable temporary
//
AutoScopedVariableManagerScope asvms(nullptr /*scope*/);
void* cppHelper = GetFastInterpThrowHelper(m_exceptionTypeId);
TestAssert(cppHelper != nullptr);
if (m_isLValueObject)
{
// The address of the LValue object is the second parameter to the copy-constructor callExpr
//
AstCallExpr* callExpr = assert_cast<AstCallExpr*>(m_operand);
AstNodeBase* expr = callExpr->GetParams()[1];
TestAssert(expr->GetTypeId().IsPointerType() && expr->GetTypeId() == m_exceptionTypeId.AddPointer());
TestAssert(thread_pochiVMContext->m_fastInterpStackFrameManager->CanReserveWithoutSpill(expr->GetTypeId()));
FastInterpSnippet snippet = expr->PrepareForFastInterp(x_FINoSpill);
TestAssert(!snippet.IsEmpty() && !snippet.IsUncontinuable());
FastInterpBoilerplateInstance* enterCppInst = thread_pochiVMContext->m_fastInterpEngine->InstantiateBoilerplate(
FastInterpBoilerplateLibrary<FICallExprEnterCppFnImpl>::SelectBoilerplateBluePrint(
TypeId::Get<void>().GetDefaultFastInterpTypeId(),
true /*isNoExcept*/));
enterCppInst->PopulateCppFnPtrPlaceholder(0, cppHelper);
FastInterpBoilerplateInstance* inst = thread_pochiVMContext->m_fastInterpEngine->InstantiateBoilerplate(
FastInterpBoilerplateLibrary<FIThrowExceptionImpl>::SelectBoilerplateBluePrint(
true /*isQuickAccess*/));
inst->PopulateBoilerplateFnPtrPlaceholder(1, enterCppInst);
snippet = snippet.AddContinuation(inst);
FastInterpBoilerplateInstance* cleanup = thread_pochiVMContext->m_scopedVariableManager.FIGenerateEHEntryPointForCurrentPosition();
inst->PopulateBoilerplateFnPtrPlaceholder(0, cleanup);
// This is weird. Despite 'throw' never returns, the caller expects it to have a continuation,
// since unlike break/continue/return, we haven't locked down unreachable code after throw.
// So just create a dummy noop operator in the end as workaround. Same for the other branch.
// TODO: lockdown unreachable code after throw, like we did for break/continue/return
//
return FastInterpSnippet {
snippet.m_entry, FIGetNoopBoilerplate()
};
}
else
{
// Allocate space to store the temporary exception object.
// It will be copy-constructed to the real exception object by the CPP helper.
// This is allowed by C++ standard: C++17 guaranteed copy-elision does not apply to throw
//
uint64_t offset = thread_pochiVMContext->m_fastInterpStackFrameManager->PushLocalVar(variable.GetTypeId().RemovePointer());
variable.SetFastInterpOffset(offset);
FastInterpSnippet snippet;
if (m_isCtor)
{
TestAssert(m_operand->GetAstNodeType() == AstNodeType::AstCallExpr);
AstCallExpr* callExpr = assert_cast<AstCallExpr*>(m_operand);
TestAssert(callExpr->GetTypeId().IsVoid());
TestAssert(callExpr->GetParams().size() > 0 && callExpr->GetParams()[0]->GetAstNodeType() == AstNodeType::AstExceptionAddressPlaceholder);
AstExceptionAddressPlaceholder* placeholder = assert_cast<AstExceptionAddressPlaceholder*>(callExpr->GetParams()[0]);
placeholder->SetFastInterpValue(offset);
snippet = callExpr->PrepareForFastInterp(x_FINoSpill);
}
else if (m_exceptionTypeId.IsCppClassType())
{
// The only way to get a RValue CPP type is from the return value of a callExpr
//
TestAssert(m_operand->GetAstNodeType() == AstNodeType::AstCallExpr);
AstCallExpr* callExpr = assert_cast<AstCallExpr*>(m_operand);
TestAssert(callExpr->IsCppFunction() && callExpr->GetCppFunctionMetadata()->m_isUsingSret);
callExpr->SetFastInterpSretVariable(&variable);
snippet = callExpr->PrepareForFastInterp(x_FINoSpill);
}
else
{
TestAssert(m_operand->GetTypeId().IsPrimitiveType() || m_operand->GetTypeId().IsPointerType());
FISpillLocation loc;
loc.SetSpillLocation(static_cast<uint32_t>(offset));
snippet = m_operand->PrepareForFastInterp(loc);
}
// Now the exception object is constructed, and shall be destructed after the exception is thrown
//
thread_pochiVMContext->m_scopedVariableManager.PushObject(&variable);
FastInterpBoilerplateInstance* enterCppInst = thread_pochiVMContext->m_fastInterpEngine->InstantiateBoilerplate(
FastInterpBoilerplateLibrary<FICallExprEnterCppFnImpl>::SelectBoilerplateBluePrint(
TypeId::Get<void>().GetDefaultFastInterpTypeId(),
true /*isNoExcept*/));
enterCppInst->PopulateCppFnPtrPlaceholder(0, cppHelper);
FastInterpBoilerplateInstance* inst = thread_pochiVMContext->m_fastInterpEngine->InstantiateBoilerplate(
FastInterpBoilerplateLibrary<FIThrowExceptionImpl>::SelectBoilerplateBluePrint(
false /*isQuickAccess*/));
inst->PopulateConstantPlaceholder<uint64_t>(0, offset);
inst->PopulateBoilerplateFnPtrPlaceholder(1, enterCppInst);
snippet = snippet.AddContinuation(inst);
// Important to call FIGenerateEHEntryPointForCurrentPosition after pushing variable to scopeVariableManager,
// since it must be destructed after exception is thrown
//
FastInterpBoilerplateInstance* cleanup = thread_pochiVMContext->m_scopedVariableManager.FIGenerateEHEntryPointForCurrentPosition();
inst->PopulateBoilerplateFnPtrPlaceholder(0, cleanup);
thread_pochiVMContext->m_fastInterpStackFrameManager->PopLocalVar(variable.GetTypeId().RemovePointer());
return FastInterpSnippet {
snippet.m_entry, FIGetNoopBoilerplate()
};
}
}
} // namespace PochiVM