https://github.com/JuliaLang/julia
Tip revision: 3b4e7b46ccf602e004905273ebebf85c47e220f7 authored by Keno Fischer on 10 October 2018, 00:26:12 UTC
HACK: Option to ignore all inlining heuristics
HACK: Option to ignore all inlining heuristics
Tip revision: 3b4e7b4
interpreter-stacktrace.c
// This file is a part of Julia. License is MIT: https://julialang.org/license
// #include'd from interpreter.c
// Backtrace support
#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || defined(_OS_WINDOWS_)
extern uintptr_t __start_jl_interpreter_frame_val;
uintptr_t __start_jl_interpreter_frame = (uintptr_t)&__start_jl_interpreter_frame_val;
extern uintptr_t __stop_jl_interpreter_frame_val;
uintptr_t __stop_jl_interpreter_frame = (uintptr_t)&__stop_jl_interpreter_frame_val;
#define SECT_INTERP JL_SECTION("jl_interpreter_frame_val")
#if defined(_CPU_X86_) && defined(_OS_WINDOWS_)
#define MANGLE(x) "@" x "@8"
#else
#define MANGLE(x) x
#endif
#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_)
#if defined(_CPU_ARM_)
# define ASM_FUNCTION_TYPE "%function"
#else
# define ASM_FUNCTION_TYPE "@function"
#endif
#define ASM_ENTRY \
".text\n" \
".p2align 4,0x90\n" \
".global enter_interpreter_frame\n" \
".type enter_interpreter_frame," ASM_FUNCTION_TYPE "\n"
#if defined(_OS_LINUX_)
#define ASM_END ".previous\n"
#else
#define ASM_END
#endif
#else
#define ASM_ENTRY \
".text\n" \
".globl enter_interpreter_frame\n"
#define ASM_END
#endif
#elif defined(_OS_DARWIN_)
extern uintptr_t __start_jl_interpreter_frame_val __asm("section$start$__TEXT$__jif");
uintptr_t __start_jl_interpreter_frame = (uintptr_t)&__start_jl_interpreter_frame_val;
extern uintptr_t __stop_jl_interpreter_frame_val __asm("section$end$__TEXT$__jif");
uintptr_t __stop_jl_interpreter_frame = (uintptr_t)&__stop_jl_interpreter_frame_val;
#define SECT_INTERP JL_SECTION("__TEXT,__jif")
#define MANGLE(x) "_" x
#define ASM_ENTRY \
".section __TEXT,__text,regular,pure_instructions\n" \
".globl _enter_interpreter_frame\n"
#define ASM_END ".previous"
#else
#define SECT_INTERP
#define NO_INTERP_BT
#warning "Interpreter backtraces not implemented for this platform"
#endif
#define STR(x) #x
#define XSTR(x) STR(x)
// This function is special. The unwinder looks for this function to find interpreter
// stack frames.
#ifdef _CPU_X86_64_
// Instructions: Make sure that MAX_INTERP_STATE_SIZE is a multiple of
// alignof(struct interpreter_state) and larger than
// sizeof(struct interpreter_state). Additionally, make sure that
// MAX_INTERP_STATE_SIZE+STACK_PADDING+8 is a multiple of 16 to
// ensure the proper stack alignment.
#define MAX_INTERP_STATE_SIZE 72
#define STACK_PADDING 0
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, "Stack layout invariants violated.");
static_assert(MAX_INTERP_STATE_SIZE % alignof(interpreter_state) == 0, "Stack layout invariants violated");
static_assert(((MAX_INTERP_STATE_SIZE + STACK_PADDING + 8) % 16) == 0, "Stack layout invariants violated");
#ifdef _OS_WINDOWS_
size_t TOTAL_STACK_PADDING = STACK_PADDING + 32;
#else
size_t TOTAL_STACK_PADDING = STACK_PADDING;
#endif
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".cfi_startproc\n"
"\tsubq $" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING)", %rsp\n"
".cfi_def_cfa_offset " XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING)" + 8\n"
#ifdef _OS_WINDOWS_
#define ARG1_REG "rcx"
#else
#define ARG1_REG "rdi"
#endif
"\tmovq %" ARG1_REG ", %rax\n"
"\tleaq " XSTR(STACK_PADDING) "(%rsp), %" ARG1_REG "\n"
// Zero out the src and mi fields
"\tmovq $0, 0(%" ARG1_REG ")\n"
"\tmovq $0, 8(%" ARG1_REG ")\n"
#ifdef _OS_WINDOWS_
// Make space for the register parameter area
"\tsubq $32, %rsp\n"
#endif
// The L here conviences the OS X linker not to terminate the unwind info early
"Lenter_interpreter_frame_start_val:\n"
"\tcallq *%rax\n"
"Lenter_interpreter_frame_end_val:\n"
#ifdef _OS_WINDOWS_
"\taddq $32, %rsp\n"
#endif
"\taddq $" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING)", %rsp\n"
#ifndef _OS_DARWIN_
// Somehow this throws off compact unwind info on OS X
".cfi_def_cfa_offset 8\n"
#endif
"\tretq\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI
#elif defined(_CPU_X86_)
#define MAX_INTERP_STATE_SIZE 36
#ifdef _OS_WINDOWS_
#define STACK_PADDING 4
#else
#define STACK_PADDING 8
#endif
size_t TOTAL_STACK_PADDING = STACK_PADDING;
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, "Stack layout invariants violated");
static_assert(MAX_INTERP_STATE_SIZE % alignof(interpreter_state) == 0, "Stack layout invariants violated");
#ifndef _OS_WINDOWS_
static_assert((MAX_INTERP_STATE_SIZE + STACK_PADDING + 4) % 16 == 0, "Stack layout invariants violated");
#endif
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".cfi_startproc\n"
#ifdef _OS_WINDOWS_
/*
* On win32, we set -mincoming-stack-boundary=2. This causes GCC to emit stack
* realignment gadgets into the prologue of every function. Unfortunately for
* us there are two different kinds of such gadgets and since we don't know
* which one the target function is going to use, we can't use the same trick
* as everywhere else. From https://gcc.gnu.org/ml/gcc/2007-12/msg00503.html,
* the two prologues are:
*
* pushl %ebp
* movl %esp, %ebp
* andl $-16, %esp
*
* and
*
* pushl %edi // Save callee save reg edi
* leal 8(%esp), %edi // Save address of parameter frame
* andl $-16, %esp // Align local stack
* pushl $4(%edi) // save return address
* pushl %ebp // save old ebp
* movl %esp, %ebp // point ebp to pseudo frame
*
* From the perspective of the unwinder, the first case looks like a regular
* (without the realignment gadget) stack frame. However, for the second one,
* the compiler deliberately constructs a "fake stack frame" that has an
* incorrect stack address for the previous frame. To work around all of this,
* use ebp based addressing on win32
*/
#define FP_CAPTURE_OFFSET MAX_INTERP_STATE_SIZE
#define ENTRY_OFFSET 8
"\tpushl %ebp\n"
".cfi_def_cfa_offset " XSTR(ENTRY_OFFSET)"\n"
"\tmovl %esp, %ebp\n"
#else
#define ENTRY_OFFSET 4
#endif
"\tsubl $" XSTR(MAX_INTERP_STATE_SIZE) ", %esp\n"
".cfi_def_cfa_offset " XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(ENTRY_OFFSET) "\n"
"\tmovl %ecx, %eax\n"
"\tmovl %esp, %ecx\n"
// Zero out the src and mi fields
"\tmovl $0, (%esp)\n"
"\tmovl $0, 4(%esp)\n"
// Restore 16 byte stack alignment
// Technically not necessary on windows, because we don't assume this
// alignment, but let's be nice if we ever start doing that.
"\tsubl $" XSTR(STACK_PADDING) ", %esp\n"
".cfi_def_cfa_offset " XSTR(STACK_PADDING) " + " XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(ENTRY_OFFSET) "\n"
"Lenter_interpreter_frame_start_val:\n"
"\tcalll *%eax\n"
"Lenter_interpreter_frame_end_val:\n"
"\taddl $" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ", %esp\n"
#ifdef _OS_WINDOWS_
".cfi_def_cfa_offset 8\n"
"\tpopl %ebp\n"
#endif
".cfi_def_cfa_offset 4\n"
"\tret\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI __attribute__((fastcall))
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, "Update assembly code above");
#elif defined(_CPU_AARCH64_)
#define MAX_INTERP_STATE_SIZE 64
#define STACK_PADDING 16
// Check that the interpreter state can fit
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE,
"Stack layout invariants violated.");
// Check that the alignment of the type is satisfied
// (16 is stack alignment at function boundary)
static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated");
static_assert(STACK_PADDING % alignof(interpreter_state) == 0,
"Stack layout invariants violated");
// Check that ABI stack alignment requirement is maintained.
static_assert(((MAX_INTERP_STATE_SIZE + STACK_PADDING) % 16) == 0,
"Stack layout invariants violated");
// Check that the padding is large enough for lr.
static_assert(STACK_PADDING >= sizeof(void*), "Stack layout invariants violated");
size_t TOTAL_STACK_PADDING = STACK_PADDING;
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".cfi_startproc\n"
// Save lr
"\tstr x30, [sp, #-(" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")]!\n"
"\t.cfi_def_cfa_offset (" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")\n"
"\t.cfi_offset 30, -(" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")\n"
"\tmov x2, x0\n"
// Zero out the src and mi fields
"\tstp xzr, xzr, [sp, " XSTR(STACK_PADDING) "]\n"
"\tadd x0, sp, " XSTR(STACK_PADDING) "\n"
"Lenter_interpreter_frame_start_val:\n"
"\tblr x2\n"
"Lenter_interpreter_frame_end_val:\n"
"\tldr x30, [sp], (" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")\n"
"\t.cfi_restore 30\n"
"\t.cfi_def_cfa_offset 0\n"
"\tret\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI
#elif defined(_CPU_ARM_)
#define MAX_INTERP_STATE_SIZE 48
// Check that the interpreter state can fit
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE,
"Stack layout invariants violated.");
// Check that the alignment of the type is satisfied
// (16 is what we realign the stack to)
static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated");
size_t TOTAL_STACK_PADDING = 0;
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".fnstart\n"
"\tpush {fp, lr}\n"
"\t.save {fp, lr}\n"
"\t.setfp fp, sp, #4\n"
"\tadd fp, sp, #4\n"
"\tmov r2, r0\n"
// Reserve enough space and realign stack to 16bytes
// The realignment is for consistency with every other architectures.
// It isn't strictly necessary since we currently do not rely on it.
"\tsub sp, sp, #" XSTR(MAX_INTERP_STATE_SIZE) "\n"
"\tbic sp, sp, #15\n"
// Zero out the src and mi field
"\tmov ip, #0\n"
"\tstr ip, [sp]\n"
"\tstr ip, [sp, #4]\n"
"\tmov r0, sp\n"
"Lenter_interpreter_frame_start_val:\n"
"\tblx r2\n"
"Lenter_interpreter_frame_end_val:\n"
"\tsub sp, fp, #4\n"
"\tpop {fp, pc}\n"
"\t.fnend\n"
ASM_END
);
#define CALLBACK_ABI
#elif defined(_CPU_PPC64_)
/**
* Implementation notes:
*
* This needs to follow the PPC ELFv2 ABI. Which means that there is a localentry
* and a global entry. The local entry expects r2/TOC to be set correctly, while
* the global entry expects r12 to be set to the function address, and from there
* restores r2/TOC. The function pointer we are getting passed point to the global
* entry and thus we need to set r12 correctly.
*
* - LR is stored in the caller
* - r1/SP is a back-chain that needs to be atomically updated
*/
#define MAX_INTERP_STATE_SIZE 64
#define MIN_STACK 32
#define STACK_PADDING 0
#define STACK_SIZE (MIN_STACK + MAX_INTERP_STATE_SIZE + STACK_PADDING)
size_t TOTAL_STACK_PADDING = MIN_STACK;
// Check that the interpreter state can fit
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE,
"Stack layout invariants violated.");
// Check that the alignment of the type is satisfied
static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated");
// Check that ABI stack alignment requirement is maintained.
static_assert(STACK_SIZE % 16 == 0, "Stack layout invariants violated");
static_assert(MIN_STACK % 16 == 0, "Stack layout invariants violated");
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
"\taddis 2, 12, .TOC.-enter_interpreter_frame@ha\n"
"\taddi 2, 2, .TOC.-enter_interpreter_frame@l\n"
"\t.localentry enter_interpreter_frame, .-enter_interpreter_frame\n"
".cfi_startproc\n"
// store LR
"\tmflr 0\n"
"\tstd 0, 16(1)\n"
".cfi_offset lr, 16\n"
// set up stack frame
"\tstdu 1, -" XSTR(STACK_SIZE) "(1)\n"
".cfi_adjust_cfa_offset " XSTR(STACK_SIZE) "\n"
"\tmtctr 3\n" // move arg1 (func pointer) to ctr
"\tmr 12, 3\n" // move func pointer to r12 if we jump to global entry point
"\tcal 3, " XSTR(MIN_STACK) "(1)\n" // move pointer to INTERP_STATE to arg1
// zero out src and mi field
"\tli 6, 0\n"
"\tstd 6, 0(3)\n"
"\tstd 6, 8(3)\n"
// store TOC
"\tstd 2, 24(1)\n"
"Lenter_interpreter_frame_start_val:\n"
"\tbctrl\n"
"Lenter_interpreter_frame_end_val:\n"
// restore TOC
"\tld 2, 24(1)\n"
// restore stack frame
"\tld 1, 0(1)\n"
// restore LR
"\tld 0, 16(1)\n"
"\tmtlr 0\n"
".cfi_same_value lr\n"
"\tblr\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI
#else
#warning "Interpreter backtraces not implemented for this platform"
#define NO_INTERP_BT
#endif
#ifndef NO_INTERP_BT
extern uintptr_t enter_interpreter_frame_start_val asm("Lenter_interpreter_frame_start_val");
extern uintptr_t enter_interpreter_frame_end_val asm("Lenter_interpreter_frame_end_val");
uintptr_t enter_interpreter_frame_start = (uintptr_t)&enter_interpreter_frame_start_val;
uintptr_t enter_interpreter_frame_end = (uintptr_t)&enter_interpreter_frame_end_val;
JL_DLLEXPORT int jl_is_interpreter_frame(uintptr_t ip)
{
return __start_jl_interpreter_frame <= ip && ip <= __stop_jl_interpreter_frame;
}
JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip)
{
return enter_interpreter_frame_start <= ip && ip <= enter_interpreter_frame_end;
}
JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining)
{
#ifdef FP_CAPTURE_OFFSET
interpreter_state *s = (interpreter_state *)(fp-FP_CAPTURE_OFFSET);
#else
interpreter_state *s = (interpreter_state *)(sp+TOTAL_STACK_PADDING);
#endif
if (space_remaining <= 1)
return 0;
// Sentinel value to indicate an interpreter frame
data[0] = (uintptr_t)-1;
data[1] = s->mi ? (uintptr_t)s->mi : s->src ? (uintptr_t)s->src : (uintptr_t)jl_nothing;
data[2] = (uintptr_t)s->ip;
return 2;
}
extern void * CALLBACK_ABI enter_interpreter_frame(void * CALLBACK_ABI (*callback)(interpreter_state *, void *), void *arg);
#else
JL_DLLEXPORT int jl_is_interpreter_frame(uintptr_t ip)
{
return 0;
}
JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip)
{
return 0;
}
JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining)
{
return 0;
}
#define CALLBACK_ABI
void *NOINLINE enter_interpreter_frame(void *(*callback)(interpreter_state *, void *), void *arg) {
interpreter_state state = {};
return callback(&state, arg);
}
#endif