Implement __artiq_personality.

This commit is contained in:
whitequark 2015-07-27 09:10:20 +03:00
parent bb5fe60137
commit 7c77dd317a
8 changed files with 373 additions and 13 deletions

View File

@ -979,10 +979,16 @@ class Raise(Terminator):
"""
:param value: (:class:`Value`) exception value
:param exn: (:class:`BasicBlock`) exceptional target
"""
def __init__(self, value, name=""):
def __init__(self, value, exn=None, name=""):
assert isinstance(value, Value)
super().__init__([value], builtins.TNone(), name)
if exn is not None:
assert isinstance(exn, BasicBlock)
operands = [value, exn]
else:
operands = [value]
super().__init__(operands, builtins.TNone(), name)
def opcode(self):
return "raise"
@ -990,6 +996,9 @@ class Raise(Terminator):
def value(self):
return self.operands[0]
def exception_target(self):
return self.operands[1]
class Invoke(Terminator):
"""
A function call operation that supports exception handling.

View File

@ -472,7 +472,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.append(ir.SetAttr(exn, "__file__", loc_file))
self.append(ir.SetAttr(exn, "__line__", loc_line))
self.append(ir.SetAttr(exn, "__col__", loc_column))
self.append(ir.Raise(exn))
if self.unwind_target:
self.append(ir.Raise(exn, self.unwind_target))
else:
self.append(ir.Raise(exn))
def visit_Raise(self, node):
self.raise_exn(self.visit(node.exc))

View File

@ -582,17 +582,30 @@ class LLVMIRGenerator:
return self.llbuilder.unreachable()
def process_Raise(self, insn):
arg = self.map(insn.operands[0])
llinsn = self.llbuilder.call(self.llbuiltin("__artiq_raise"), [arg],
name=insn.name)
llexn = self.map(insn.value())
if insn.exception_target() is not None:
llnormalblock = self.llfunction.append_basic_block("unreachable")
llnormalblock.terminator = ll.Unreachable(llnormalblock)
llnormalblock.instructions.append(llnormalblock.terminator)
llunwindblock = self.map(insn.exception_target())
llinsn = self.llbuilder.invoke(self.llbuiltin("__artiq_raise"), [llexn],
llnormalblock, llunwindblock,
name=insn.name)
else:
llinsn = self.llbuilder.call(self.llbuiltin("__artiq_raise"), [llexn],
name=insn.name)
self.llbuilder.unreachable()
llinsn.attributes.add('noreturn')
self.llbuilder.unreachable()
return llinsn
def process_LandingPad(self, insn):
lllandingpad = self.llbuilder.landingpad(ll.LiteralStructType([ll.IntType(8).as_pointer()]),
# Layout on return from landing pad: {%_Unwind_Exception*, %Exception*}
lllandingpadty = ll.LiteralStructType([ll.IntType(8).as_pointer(),
ll.IntType(8).as_pointer()])
lllandingpad = self.llbuilder.landingpad(lllandingpadty,
self.llbuiltin("__artiq_personality"))
llrawexn = self.llbuilder.extract_value(lllandingpad, 0)
llrawexn = self.llbuilder.extract_value(lllandingpad, 1)
llexn = self.llbuilder.bitcast(llrawexn, self.llty_of_type(insn.type))
llexnnameptr = self.llbuilder.gep(llexn, [self.llindex(0), self.llindex(0)])
llexnname = self.llbuilder.load(llexnnameptr)

View File

@ -1,4 +1,4 @@
CC ?= clang
libartiq_personality.so: ../../soc/runtime/artiq_personality.c artiq_terminate.c
$(CC) -Wall -Werror -I. -I../../soc/runtime -fPIC -shared -o $@ $^
$(CC) -Wall -Werror -I. -I../../soc/runtime -g -fPIC -shared -o $@ $^

View File

@ -6,7 +6,7 @@
void __artiq_terminate(struct artiq_exception *exn) {
printf("Uncaught %s: %s (%"PRIi64", %"PRIi64", %"PRIi64")\n"
"at %s:%"PRIi32":%"PRIi32"",
"at %s:%"PRIi32":%"PRIi32"\n",
exn->name, exn->message,
exn->param[0], exn->param[1], exn->param[1],
exn->file, exn->line, exn->column + 1);

View File

@ -0,0 +1,8 @@
# RUN: %python -m artiq.compiler.testbench.jit %s
# REQUIRES: exceptions
try:
1/0
except ZeroDivisionError:
# CHECK-L: OK
print("OK")

View File

@ -5,6 +5,198 @@
#include <unwind.h>
#include "artiq_personality.h"
/* Logging */
#ifndef NDEBUG
#define EH_LOG0(fmt) fprintf(stderr, "__artiq_personality: " fmt "\n")
#define EH_LOG(fmt, ...) fprintf(stderr, "__artiq_personality: " fmt "\n", __VA_ARGS__)
#else
#define EH_LOG0(fmt)
#define EH_LOG(fmt, ...)
#endif
#define EH_FAIL(err) \
do { \
fprintf(stderr, "__artiq_personality fatal: %s\n", err); \
abort(); \
} while(0)
#define EH_ASSERT(expr) \
if(!(expr)) EH_FAIL(#expr)
/* DWARF format handling */
enum {
DW_EH_PE_absptr = 0x00,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
DW_EH_PE_indirect = 0x80,
DW_EH_PE_omit = 0xFF
};
// Read a uleb128 encoded value and advance pointer
// See Variable Length Data in: http://dwarfstd.org/Dwarf3.pdf
static uintptr_t readULEB128(const uint8_t **data) {
uintptr_t result = 0;
uintptr_t shift = 0;
unsigned char byte;
const uint8_t *p = *data;
do {
byte = *p++;
result |= (byte & 0x7f) << shift;
shift += 7;
}
while (byte & 0x80);
*data = p;
return result;
}
// Read a sleb128 encoded value and advance pointer
// See Variable Length Data in: http://dwarfstd.org/Dwarf3.pdf
static uintptr_t readSLEB128(const uint8_t **data) {
uintptr_t result = 0;
uintptr_t shift = 0;
unsigned char byte;
const uint8_t *p = *data;
do {
byte = *p++;
result |= (byte & 0x7f) << shift;
shift += 7;
}
while (byte & 0x80);
*data = p;
if ((byte & 0x40) && (shift < (sizeof(result) << 3))) {
result |= (~0 << shift);
}
return result;
}
static unsigned getEncodingSize(uint8_t Encoding) {
if (Encoding == DW_EH_PE_omit)
return 0;
switch (Encoding & 0x0F) {
case DW_EH_PE_absptr:
return sizeof(uintptr_t);
case DW_EH_PE_udata2:
return sizeof(uint16_t);
case DW_EH_PE_udata4:
return sizeof(uint32_t);
case DW_EH_PE_udata8:
return sizeof(uint64_t);
case DW_EH_PE_sdata2:
return sizeof(int16_t);
case DW_EH_PE_sdata4:
return sizeof(int32_t);
case DW_EH_PE_sdata8:
return sizeof(int64_t);
default:
// not supported
abort();
}
}
// Read a pointer encoded value and advance pointer
// See Variable Length Data in: http://dwarfstd.org/Dwarf3.pdf
static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
uintptr_t result = 0;
const uint8_t *p = *data;
if (encoding == DW_EH_PE_omit)
return(result);
// first get value
switch (encoding & 0x0F) {
case DW_EH_PE_absptr:
result = *((uintptr_t*)p);
p += sizeof(uintptr_t);
break;
case DW_EH_PE_uleb128:
result = readULEB128(&p);
break;
// Note: This case has not been tested
case DW_EH_PE_sleb128:
result = readSLEB128(&p);
break;
case DW_EH_PE_udata2:
result = *((uint16_t*)p);
p += sizeof(uint16_t);
break;
case DW_EH_PE_udata4:
result = *((uint32_t*)p);
p += sizeof(uint32_t);
break;
case DW_EH_PE_udata8:
result = *((uint64_t*)p);
p += sizeof(uint64_t);
break;
case DW_EH_PE_sdata2:
result = *((int16_t*)p);
p += sizeof(int16_t);
break;
case DW_EH_PE_sdata4:
result = *((int32_t*)p);
p += sizeof(int32_t);
break;
case DW_EH_PE_sdata8:
result = *((int64_t*)p);
p += sizeof(int64_t);
break;
default:
// not supported
abort();
break;
}
// then add relative offset
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
// do nothing
break;
case DW_EH_PE_pcrel:
result += (uintptr_t)(*data);
break;
case DW_EH_PE_textrel:
case DW_EH_PE_datarel:
case DW_EH_PE_funcrel:
case DW_EH_PE_aligned:
default:
// not supported
abort();
break;
}
// then apply indirection
if (encoding & DW_EH_PE_indirect) {
result = *((uintptr_t*)result);
}
*data = p;
return result;
}
/* Raising and catching */
#define ARTIQ_EXCEPTION_CLASS 0x4152545141525451LL // 'ARTQARTQ'
struct artiq_raised_exception {
@ -37,5 +229,140 @@ void __artiq_raise(struct artiq_exception *artiq_exn) {
_Unwind_Reason_Code __artiq_personality(
int version, _Unwind_Action actions, uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) {
abort();
EH_LOG("entry (actions =%s%s%s%s; class=%08lx; object=%p, context=%p)",
(actions & _UA_SEARCH_PHASE ? " search" : ""),
(actions & _UA_CLEANUP_PHASE ? " cleanup" : ""),
(actions & _UA_HANDLER_FRAME ? " handler" : ""),
(actions & _UA_FORCE_UNWIND ? " force-unwind" : ""),
exceptionClass, exceptionObject, context);
EH_ASSERT((exceptionClass == ARTIQ_EXCEPTION_CLASS) &&
"Foreign exceptions are not supported");
struct artiq_raised_exception *inflight =
(struct artiq_raised_exception*)exceptionObject;
EH_LOG("exception name=%s",
inflight->artiq.name);
// Get a pointer to LSDA. If there's no LSDA, this function doesn't
// actually handle any exceptions.
const uint8_t *lsda = (const uint8_t*) _Unwind_GetLanguageSpecificData(context);
if(lsda == NULL)
return _URC_CONTINUE_UNWIND;
EH_LOG("lsda=%p", lsda);
// Get the current instruction pointer and offset it before next
// instruction in the current frame which threw the exception.
uintptr_t pc = _Unwind_GetIP(context) - 1;
// Get beginning of the current frame's code.
uintptr_t funcStart = _Unwind_GetRegionStart(context);
uintptr_t pcOffset = pc - funcStart;
EH_LOG("pc=%p (%p+%p)", (void*)pc, (void*)funcStart, (void*)pcOffset);
// Parse LSDA header.
uint8_t lpStartEncoding = *lsda++;
if (lpStartEncoding != DW_EH_PE_omit) {
readEncodedPointer(&lsda, lpStartEncoding);
}
uint8_t ttypeEncoding = *lsda++;
const uint8_t *classInfo = NULL;
if (ttypeEncoding != DW_EH_PE_omit) {
// Calculate type info locations in emitted dwarf code which
// were flagged by type info arguments to llvm.eh.selector
// intrinsic
uintptr_t classInfoOffset = readULEB128(&lsda);
classInfo = lsda + classInfoOffset;
EH_LOG("classInfo=%p", classInfo);
}
// Walk call-site table looking for range that includes current PC.
uint8_t callSiteEncoding = *lsda++;
uint32_t callSiteTableLength = readULEB128(&lsda);
const uint8_t *callSiteTableStart = lsda;
const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
const uint8_t *actionTableStart = callSiteTableEnd;
const uint8_t *callSitePtr = callSiteTableStart;
while (callSitePtr < callSiteTableEnd) {
uintptr_t start = readEncodedPointer(&callSitePtr,
callSiteEncoding);
uintptr_t length = readEncodedPointer(&callSitePtr,
callSiteEncoding);
uintptr_t landingPad = readEncodedPointer(&callSitePtr,
callSiteEncoding);
uintptr_t actionValue = readULEB128(&callSitePtr);
EH_LOG("call site (start=+%p, len=%d, landingPad=+%p, actionValue=%d)",
(void*)start, (int)length, (void*)landingPad, (int)actionValue);
if(landingPad == 0) {
EH_LOG0("no landing pad, skipping");
continue;
}
if ((start <= pcOffset) && (pcOffset < (start + length))) {
EH_LOG0("call site matches pc");
int exceptionMatched = 0;
if(actionValue) {
const uint8_t *actionEntry = actionTableStart + (actionValue - 1);
EH_LOG("actionEntry=%p", actionEntry);
for(;;) {
// Each emitted DWARF action corresponds to a 2 tuple of
// type info address offset, and action offset to the next
// emitted action.
intptr_t typeInfoOffset = readSLEB128(&actionEntry);
const uint8_t *tempActionEntry = actionEntry;
intptr_t actionOffset = readSLEB128(&tempActionEntry);
EH_LOG("typeInfoOffset=%p actionOffset=%p",
(void*)typeInfoOffset, (void*)actionOffset);
EH_ASSERT((typeInfoOffset >= 0) && "Filter clauses are not supported");
unsigned encodingSize = getEncodingSize(ttypeEncoding);
const uint8_t *typeInfoPtrPtr = classInfo - typeInfoOffset * encodingSize;
uintptr_t typeInfoPtr = readEncodedPointer(&typeInfoPtrPtr, ttypeEncoding);
EH_LOG("encodingSize=%u typeInfoPtrPtr=%p typeInfoPtr=%p",
encodingSize, typeInfoPtrPtr, (void*)typeInfoPtr);
EH_LOG("typeInfo=%s", (char*)typeInfoPtr);
if(inflight->artiq.typeinfo == typeInfoPtr) {
EH_LOG0("matching action found");
exceptionMatched = 1;
break;
}
if (!actionOffset)
break;
actionEntry += actionOffset;
}
}
if (!(actions & _UA_SEARCH_PHASE)) {
EH_LOG0("jumping to landing pad");
_Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
(uintptr_t)exceptionObject);
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
(uintptr_t)&inflight->artiq);
_Unwind_SetIP(context, funcStart + landingPad);
return _URC_INSTALL_CONTEXT;
} else if(exceptionMatched) {
EH_LOG0("handler found");
return _URC_HANDLER_FOUND;
} else {
EH_LOG0("handler not found");
return _URC_CONTINUE_UNWIND;
}
}
}
return _URC_CONTINUE_UNWIND;
}

View File

@ -5,7 +5,7 @@
struct artiq_exception {
union {
void *typeinfo;
uintptr_t typeinfo;
const char *name;
};
const char *file;