Skip to content
Draft
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
19 changes: 19 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ ifeq ($(OS),WINNT)
SRCS += win32_ucontext
endif

# Assembly sources for minimal setjmp (x86_64 Linux only for now)
ASM_SRCS :=
ifeq ($(OS),Linux)
ifeq ($(ARCH),x86_64)
ASM_SRCS += _jlsetjmp
endif
endif

ifeq ($(WITH_DTRACE),1)
DTRACE_HEADERS := uprobes.h.gen
ifneq ($(OS),Darwin)
Expand Down Expand Up @@ -229,6 +237,12 @@ CG_RELEASE_LIBS := $(COMMON_LIBPATHS) $(CG_LIBS) -ljulia -ljulia-internal
OBJS := $(SRCS:%=$(BUILDDIR)/%.o)
DOBJS := $(SRCS:%=$(BUILDDIR)/%.dbg.obj)

# Assembly object files
ASM_OBJS := $(ASM_SRCS:%=$(BUILDDIR)/%.o)
ASM_DOBJS := $(ASM_SRCS:%=$(BUILDDIR)/%.dbg.obj)
OBJS += $(ASM_OBJS)
DOBJS += $(ASM_DOBJS)

CODEGEN_OBJS := $(CODEGEN_SRCS:%=$(BUILDDIR)/%.o)
CODEGEN_DOBJS := $(CODEGEN_SRCS:%=$(BUILDDIR)/%.dbg.obj)

Expand Down Expand Up @@ -313,6 +327,11 @@ $(BUILDDIR)/%.o: $(SRCDIR)/%.cpp $(SRCDIR)/llvm-version.h $(HEADERS) $(LLVM_CONF
@$(call PRINT_CC, $(CXX) $(LLVM_CXXFLAGS) $(SHIPFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(CXX_DISABLE_ASSERTION) -c $< -o $@)
$(BUILDDIR)/%.dbg.obj: $(SRCDIR)/%.cpp $(SRCDIR)/llvm-version.h $(HEADERS) $(LLVM_CONFIG_ABSOLUTE) | $(BUILDDIR)
@$(call PRINT_CC, $(CXX) $(LLVM_CXXFLAGS) $(DEBUGFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) -c $< -o $@)
# Assembly source file rules
$(BUILDDIR)/%.o: $(SRCDIR)/%.S | $(BUILDDIR)
@$(call PRINT_CC, $(CC) $(JCPPFLAGS) $(SHIPFLAGS) -c $< -o $@)
$(BUILDDIR)/%.dbg.obj: $(SRCDIR)/%.S | $(BUILDDIR)
@$(call PRINT_CC, $(CC) $(JCPPFLAGS) $(DEBUGFLAGS) -c $< -o $@)
$(BUILDDIR)/%.o : $(SRCDIR)/%.d
@$(call PRINT_DTRACE, $(DTRACE) -G -s $< -o $@)
$(BUILDDIR)/%.dbg.obj : $(SRCDIR)/%.d
Expand Down
67 changes: 67 additions & 0 deletions src/_jlsetjmp.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

// Minimal setjmp/longjmp implementation for x86_64 Linux
// These are designed to work with the preserve_none calling convention,
// where all registers are caller-saved. This means we only need to save
// the stack pointer - the compiler will spill all live registers before
// calling setjmp.
//
// IMPORTANT: These functions ONLY work correctly when called with the
// preserve_none calling convention. Without preserve_none, callee-saved
// registers won't be restored properly after longjmp.

#if defined(__x86_64__) && defined(__linux__)

// Mark stack as non-executable
.section .note.GNU-stack,"",@progbits

.text

// ============================================================================
// jl_minimal_setjmp / ijl_minimal_setjmp
// ============================================================================
.p2align 4,0x90
.globl ijl_minimal_setjmp
.globl jl_minimal_setjmp
.type ijl_minimal_setjmp,@function
.type jl_minimal_setjmp,@function
ijl_minimal_setjmp:
jl_minimal_setjmp:
// N.B: In preserve_none ABI, r12 is the first argument.
// r12 = pointer to buffer (three pointer-sized slots: rbp, rsp, rip)
// Save RSP as it was *before* the call instruction pushed the return address.
lea 8(%rsp), %rax // rax = original RSP (before call)
mov %rbp, 0(%r12) // save RBP to buffer[0]
mov %rax, 8(%r12) // save RSP to buffer[1]
mov (%rsp), %rax // rax = return address
mov %rax, 16(%r12) // save return address to buffer[2]
xor %eax, %eax // return 0 (setjmp returns 0 on first call)
ret
.size ijl_minimal_setjmp, . - ijl_minimal_setjmp
.size jl_minimal_setjmp, . - jl_minimal_setjmp


// ============================================================================
// jl_minimal_longjmp / ijl_minimal_longjmp
// ============================================================================
.p2align 4,0x90
.globl ijl_minimal_longjmp
.globl jl_minimal_longjmp
.type ijl_minimal_longjmp,@function
.type jl_minimal_longjmp,@function
ijl_minimal_longjmp:
jl_minimal_longjmp:
// rdi = pointer to buffer (two pointer-sized slots: rsp, rip)
// esi = return value (passed to setjmp caller)
mov %esi, %eax // set return value
test %eax, %eax // longjmp must return non-zero
jne 1f
inc %eax // if val was 0, return 1
1:
mov 0(%rdi), %rbp // restore RBP
mov 8(%rdi), %rsp // restore RSP (to pre-call value)
jmp *16(%rdi) // jump to saved return address
.size ijl_minimal_longjmp, . - ijl_minimal_longjmp
.size jl_minimal_longjmp, . - jl_minimal_longjmp

#endif
54 changes: 48 additions & 6 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,14 @@ static const auto jlenter_func = new JuliaFunction<>{
{T_pjlvalue, getPointerTy(C)}, false); },
nullptr,
};
static const auto jlentermin_func = new JuliaFunction<>{
XSTR(jl_enter_min_handler),
[](LLVMContext &C) {
auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C);
return FunctionType::get(getVoidTy(C),
{T_pjlvalue, getPointerTy(C)}, false); },
nullptr,
};
static const auto jl_current_exception_func = new JuliaFunction<>{
XSTR(jl_current_exception),
[](LLVMContext &C) { return FunctionType::get(JuliaType::get_prjlvalue_ty(C), {JuliaType::get_pjlvalue_ty(C)}, false); },
Expand Down Expand Up @@ -1239,6 +1247,17 @@ static const auto jl_object_id__func = new JuliaFunction<TypeFnContextAndSizeT>{
{T_size, PointerType::get(C, AddressSpace::Derived)}, false); },
nullptr,
};
static const auto setjmp_min_func = new JuliaFunction<TypeFnContextAndTriple>{
XSTR(jl_minimal_setjmp),
[](LLVMContext &C, const Triple &T) {
return FunctionType::get(getInt32Ty(C),
{getPointerTy(C)}, false);
},
[](LLVMContext &C) { return AttributeList::get(C,
Attributes(C, {Attribute::ReturnsTwice}),
AttributeSet(),
None); },
};
static const auto setjmp_func = new JuliaFunction<TypeFnContextAndTriple>{
jl_setjmp_name,
[](LLVMContext &C, const Triple &T) {
Expand Down Expand Up @@ -6151,8 +6170,13 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result)
}
}
ctx.builder.CreateCall(prepare_call(jlleave_noexcept_func), {get_current_task(ctx), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), handler_to_end.size())});
#ifdef JL_HAVE_MIN_SETJMP
auto *handler_sz64 = ConstantInt::get(Type::getInt64Ty(ctx.builder.getContext()),
sizeof(struct _jl_handler_min_setjmp));
#else
auto *handler_sz64 = ConstantInt::get(Type::getInt64Ty(ctx.builder.getContext()),
sizeof(jl_handler_t));
sizeof(struct _jl_handler_setjmp));
#endif
for (AllocaInst *handler : handler_to_end) {
ctx.builder.CreateLifetimeEnd(handler, handler_sz64);
}
Expand Down Expand Up @@ -9355,17 +9379,31 @@ static jl_llvm_functions_t
ctx.ssavalue_assigned[cursor] = true;
// Actually enter the exception frame
auto ct = get_current_task(ctx);
#if JL_HAVE_MIN_SETJMP
auto *handler_sz64 = ConstantInt::get(Type::getInt64Ty(ctx.builder.getContext()),
sizeof(struct _jl_handler_min_setjmp));
AllocaInst* ehbuff = emit_static_alloca(ctx, sizeof(struct _jl_handler_min_setjmp), Align(16));
#else
auto *handler_sz64 = ConstantInt::get(Type::getInt64Ty(ctx.builder.getContext()),
sizeof(jl_handler_t));
AllocaInst* ehbuff = emit_static_alloca(ctx, sizeof(jl_handler_t), Align(16));
sizeof(struct _jl_handler_setjmp));
AllocaInst* ehbuff = emit_static_alloca(ctx, sizeof(struct _jl_handler_setjmp), Align(16));
#endif
ctx.eh_buffers[stmt] = ehbuff;
ctx.builder.CreateLifetimeStart(ehbuff, handler_sz64);
ctx.builder.CreateCall(prepare_call(jlenter_func), {ct, ehbuff});
CallInst *sj;
#if JL_HAVE_MIN_SETJMP
ctx.builder.CreateCall(prepare_call(jlentermin_func), {ct, ehbuff});
Value *jmpbuf = emit_ptrgep(ctx, ehbuff, offsetof(struct _jl_handler_min_setjmp, min_eh_ctx));
sj = ctx.builder.CreateCall(prepare_call(setjmp_min_func), {jmpbuf});
sj->setCallingConv(CallingConv::PreserveNone);
#else
Value *jmpbuf = emit_ptrgep(ctx, ehbuff, offsetof(struct _jl_handler_setjmp, eh_ctx));
ctx.builder.CreateCall(prepare_call(jlenter_func), {ct, ehbuff});
if (ctx.emission_context.TargetTriple.isOSWindows())
sj = ctx.builder.CreateCall(prepare_call(setjmp_func), {ehbuff});
sj = ctx.builder.CreateCall(prepare_call(setjmp_func), {jmpbuf});
else
sj = ctx.builder.CreateCall(prepare_call(setjmp_func), {ehbuff, ConstantInt::get(Type::getInt32Ty(ctx.builder.getContext()), 0)});
sj = ctx.builder.CreateCall(prepare_call(setjmp_func), {jmpbuf, ConstantInt::get(Type::getInt32Ty(ctx.builder.getContext()), 0)});
#endif
// We need to mark this on the call site as well. See issue #6757
sj->setCanReturnTwice();
Value *isz = ctx.builder.CreateICmpEQ(sj, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0));
Expand Down Expand Up @@ -9994,6 +10032,9 @@ static void init_jit_functions(void)
add_named_global(jlnew_func, &jl_new_structv);
add_named_global(jlsplatnew_func, &jl_new_structt);
add_named_global(setjmp_func, &jl_setjmp_f);
#if JL_HAVE_MIN_SETJMP
add_named_global(setjmp_min_func, &jl_minimal_setjmp);
#endif
add_named_global(memcmp_func, &memcmp);
add_named_global(jltypeerror_func, &jl_type_error);
add_named_global(jlcheckassign_func, &jl_checked_assignment);
Expand All @@ -10009,6 +10050,7 @@ static void init_jit_functions(void)
add_named_global(jlmethod_func, &jl_method_def);
add_named_global(jlgenericfunction_func, &jl_declare_const_gf);
add_named_global(jlenter_func, &jl_enter_handler);
add_named_global(jlentermin_func, &jl_enter_min_handler);
add_named_global(jl_current_exception_func, &jl_current_exception);
add_named_global(jlleave_noexcept_func, &jl_pop_handler_noexcept);
add_named_global(jlleave_func, &jl_pop_handler);
Expand Down
20 changes: 10 additions & 10 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ static size_t eval_phi(jl_array_t *stmts, interpreter_state *s, size_t ns, size_

static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, int toplevel)
{
jl_handler_t __eh;
jl_handler_preferred_t __eh;
size_t ns = jl_array_nrows(stmts);
jl_task_t *ct = jl_current_task;

Expand Down Expand Up @@ -506,7 +506,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
s->locals[jl_source_nslots(s->src) + id] = val;
}
else if (jl_is_enternode(stmt)) {
jl_enter_handler(ct, &__eh);
jl_enter_handler(ct, &__eh._handler);
// This is a bit tricky, but supports the implementation of PhiC nodes.
// They are conceptually slots, but the slot to store to doesn't get explicitly
// mentioned in the store (aka the "UpsilonNode") (this makes them integrate more
Expand Down Expand Up @@ -545,29 +545,29 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
// replaced later
JL_GC_PUSH1(&scope);
ct->scope = scope;
if (!jl_setjmp(__eh.eh_ctx, 0)) {
ct->eh = &__eh;
if (!JL_EH_SETJMP(__eh)) {
ct->eh = &__eh._handler;
eval_body(stmts, s, next_ip, toplevel);
jl_unreachable();
}
JL_GC_POP();
}
else {
if (!jl_setjmp(__eh.eh_ctx, 0)) {
ct->eh = &__eh;
if (!JL_EH_SETJMP(__eh)) {
ct->eh = &__eh._handler;
eval_body(stmts, s, next_ip, toplevel);
jl_unreachable();
}
}

if (s->continue_at) { // means we reached a :leave expression
jl_eh_restore_state_noexcept(ct, &__eh);
jl_eh_restore_state_noexcept(ct, &__eh._handler);
ip = s->continue_at;
s->continue_at = 0;
continue;
}
else { // a real exception
jl_eh_restore_state(ct, &__eh);
jl_eh_restore_state(ct, &__eh._handler);
ip = catch_ip;
assert(jl_enternode_catch_dest(stmt) != 0);
continue;
Expand Down Expand Up @@ -617,8 +617,8 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
// leave happens during normal control flow, but we must
// longjmp to pop the eval_body call for each enter.
s->continue_at = next_ip;
asan_unpoison_task_stack(ct, &eh->eh_ctx);
jl_longjmp(eh->eh_ctx, 1);
asan_unpoison_eh_task_stack(ct, eh);
jl_eh_longjmp(eh);
}
}
else if (head == jl_pop_exception_sym) {
Expand Down
3 changes: 3 additions & 0 deletions src/jl_exported_funcs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
XX(jl_egal) \
XX(jl_egal__bits) \
XX(jl_egal__bitstag) \
XX(jl_eh_longjmp) \
XX(jl_eh_restore_state) \
XX(jl_eh_restore_state_noexcept) \
XX(jl_enter_handler) \
Expand Down Expand Up @@ -301,6 +302,8 @@
XX(jl_maxrss) \
XX(jl_method_def) \
XX(jl_method_instance_add_backedge) \
XX(jl_minimal_longjmp) \
XX(jl_minimal_setjmp) \
XX(jl_method_table_add_backedge) \
XX(jl_method_table_disable) \
XX(jl_method_table_for) \
Expand Down
Loading