Skip to content

Instantly share code, notes, and snippets.

@bew
Last active March 9, 2024 17:59
Show Gist options
  • Save bew/1a55c69fe7cff0007d5be39fe8752da5 to your computer and use it in GitHub Desktop.
Save bew/1a55c69fe7cff0007d5be39fe8752da5 to your computer and use it in GitHub Desktop.
Minimal noprelude exception example in Crystal, with the generated LLVM IR code
# Compile it with: crystal build --prelude=empty --emit llvm-ir noprelude_exception.cr
lib LibC
alias SizeT = UInt64
fun printf(format : UInt8*, ...) : Int32
fun exit(exit_code : Int32) : NoReturn
end
require "callstack/lib_unwind"
struct UInt64
def self.zero
0
end
end
class String
def to_unsafe
pointerof(@c)
end
end
fun __crystal_personality(version : Int32, actions : LibUnwind::Action, exception_class : UInt64, exception_object : LibUnwind::Exception*, context : Void*) : LibUnwind::ReasonCode
return LibUnwind::ReasonCode::CONTINUE_UNWIND
end
@[Raises]
fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn
ret = LibUnwind.raise_exception(unwind_ex)
LibC.printf "Failed to raise an exception: %d\n".to_unsafe, ret
#CallStack.print_backtrace
LibC.exit(ret.value)
end
def raise(str : String) : NoReturn
unwind_ex = LibUnwind::Exception.new
unwind_ex.exception_class = LibC::SizeT.zero
unwind_ex.exception_cleanup = LibC::SizeT.zero
unwind_ex.exception_object = str.object_id
unwind_ex.exception_type_id = str.crystal_type_id
__crystal_raise(pointerof(unwind_ex))
end
begin
var = 2
LibC.printf("in begin: var = %d\n", var)
raise "I'm an exception!"
ensure
var = 3
LibC.printf("in ensure: var = %d\n", var)
end
LibC.printf("after: var = %d\n", var)
; ModuleID = 'main_module'
source_filename = "main_module"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
%String = type { i32, i32, i32, i8 }
%landing_pad = type { i8*, i32 }
%"struct.LibUnwind::Exception" = type { i64, i64, i64, i64, i64, i32 }
@ARGC_UNSAFE = internal global i32 0
@ARGV_UNSAFE = internal global i8** null
@"LibUnwind::Action::SEARCH_PHASE" = internal constant i32 1
@"LibUnwind::Action::CLEANUP_PHASE" = internal constant i32 2
@"LibUnwind::Action::HANDLER_FRAME" = internal constant i32 4
@"LibUnwind::Action::FORCE_UNWIND" = internal constant i32 8
@"LibUnwind::Action::END_OF_STACK" = internal constant i32 16
@"LibUnwind::Action::None" = internal constant i32 0
@"LibUnwind::Action::All" = internal constant i32 31
@"LibUnwind::ReasonCode::NO_REASON" = internal constant i32 0
@"LibUnwind::ReasonCode::FOREIGN_EXCEPTION_CAUGHT" = internal constant i32 1
@"LibUnwind::ReasonCode::FATAL_PHASE2_ERROR" = internal constant i32 2
@"LibUnwind::ReasonCode::FATAL_PHASE1_ERROR" = internal constant i32 3
@"LibUnwind::ReasonCode::NORMAL_STOP" = internal constant i32 4
@"LibUnwind::ReasonCode::END_OF_STACK" = internal constant i32 5
@"LibUnwind::ReasonCode::HANDLER_FOUND" = internal constant i32 6
@"LibUnwind::ReasonCode::INSTALL_CONTEXT" = internal constant i32 7
@"LibUnwind::ReasonCode::CONTINUE_UNWIND" = internal constant i32 8
@"'in begin: var = %d\0A...'" = private constant { i32, i32, i32, [20 x i8] } { i32 1, i32 19, i32 19, [20 x i8] c"in begin: var = %d\0A\00" }
@"'I'm an exception!'" = private constant { i32, i32, i32, [18 x i8] } { i32 1, i32 17, i32 17, [18 x i8] c"I'm an exception!\00" }
@"'in ensure: var = %d...'" = private constant { i32, i32, i32, [21 x i8] } { i32 1, i32 20, i32 20, [21 x i8] c"in ensure: var = %d\0A\00" }
@"LibUnwind::ReasonCode::CONTINUE_UNWIND:init" = internal global i1 false
@"'Failed to raise an ...'" = private constant { i32, i32, i32, [34 x i8] } { i32 1, i32 33, i32 33, [34 x i8] c"Failed to raise an exception: %d\0A\00" }
define internal i32 @__crystal_main(i32 %argc, i8** %argv) personality i32 (i32, i32, i64, %"struct.LibUnwind::Exception"*, i8*)* @__crystal_personality !dbg !4 {
alloca:
%var = alloca i32, !dbg !9
br label %entry
entry: ; preds = %alloca
store i32 %argc, i32* @ARGC_UNSAFE
store i8** %argv, i8*** @ARGV_UNSAFE
store i32 2, i32* %var, !dbg !12
%0 = call i8* @"*String#to_unsafe:Pointer(UInt8)"(%String* bitcast ({ i32, i32, i32, [20 x i8] }* @"'in begin: var = %d\0A...'" to %String*)), !dbg !13
%1 = load i32, i32* %var, !dbg !13
%2 = call i32 (i8*, ...) @printf(i8* %0, i32 %1), !dbg !14
invoke void @"*raise<String>:NoReturn"(%String* bitcast ({ i32, i32, i32, [18 x i8] }* @"'I'm an exception!'" to %String*))
to label %invoke_out unwind label %rescue, !dbg !15
rescue: ; preds = %entry
%3 = landingpad %landing_pad
cleanup, !dbg !15
%4 = extractvalue %landing_pad %3, 0, !dbg !15
%5 = extractvalue %landing_pad %3, 1, !dbg !15
store i32 3, i32* %var, !dbg !16
%6 = call i8* @"*String#to_unsafe:Pointer(UInt8)"(%String* bitcast ({ i32, i32, i32, [21 x i8] }* @"'in ensure: var = %d...'" to %String*)), !dbg !17
%7 = load i32, i32* %var, !dbg !17
%8 = call i32 (i8*, ...) @printf(i8* %6, i32 %7), !dbg !18
%9 = bitcast i8* %4 to %"struct.LibUnwind::Exception"*, !dbg !18
call void @__crystal_raise(%"struct.LibUnwind::Exception"* %9), !dbg !12
unreachable, !dbg !12
invoke_out: ; preds = %entry
unreachable, !dbg !15
}
declare i32 @printf(i8*, ...)
; Function Attrs: uwtable
define i32 @__crystal_personality(i32 %version, i32 %actions, i64 %exception_class, %"struct.LibUnwind::Exception"* %exception_object, i8* %context) #0 !dbg !19 {
entry:
%0 = call i32* @"~LibUnwind::ReasonCode::CONTINUE_UNWIND:read"(), !dbg !20
%1 = load i32, i32* %0, !dbg !20
ret i32 %1, !dbg !20
}
; Function Attrs: noreturn uwtable
define void @__crystal_raise(%"struct.LibUnwind::Exception"* %unwind_ex) #1 !dbg !21 {
alloca:
%ret = alloca i32, !dbg !22
br label %entry
entry: ; preds = %alloca
%0 = call i32 @_Unwind_RaiseException(%"struct.LibUnwind::Exception"* %unwind_ex), !dbg !23
store i32 %0, i32* %ret, !dbg !24
%1 = call i8* @"*String#to_unsafe:Pointer(UInt8)"(%String* bitcast ({ i32, i32, i32, [34 x i8] }* @"'Failed to raise an ...'" to %String*)), !dbg !25
%2 = load i32, i32* %ret, !dbg !25
%3 = call i32 (i8*, ...) @printf(i8* %1, i32 %2), !dbg !26
%4 = load i32, i32* %ret, !dbg !26
call void @exit(i32 %4), !dbg !27
unreachable, !dbg !27
}
; Function Attrs: uwtable
define i32 @main(i32 %argc, i8** %argv) #0 !dbg !28 {
entry:
%0 = call i32 @__crystal_main(i32 %argc, i8** %argv), !dbg !30
ret i32 0, !dbg !31
}
; Function Attrs: uwtable
define internal i8* @"*String#to_unsafe:Pointer(UInt8)"(%String* %self) #0 !dbg !32 {
entry:
%0 = getelementptr inbounds %String, %String* %self, i32 0, i32 3, !dbg !33
ret i8* %0, !dbg !34
}
; Function Attrs: noreturn uwtable
define internal void @"*raise<String>:NoReturn"(%String* %str) #1 !dbg !35 {
alloca:
%unwind_ex = alloca %"struct.LibUnwind::Exception", !dbg !36
%0 = alloca %"struct.LibUnwind::Exception", !dbg !37
br label %entry
entry: ; preds = %alloca
%1 = call %"struct.LibUnwind::Exception" @"*struct.LibUnwind::Exception::new:struct.LibUnwind::Exception"(), !dbg !37
store %"struct.LibUnwind::Exception" %1, %"struct.LibUnwind::Exception"* %0, !dbg !37
%2 = load %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %0, !dbg !38
store %"struct.LibUnwind::Exception" %2, %"struct.LibUnwind::Exception"* %unwind_ex, !dbg !38
%3 = getelementptr inbounds %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %unwind_ex, i32 0, i32 0, !dbg !38
store i64 0, i64* %3, !dbg !38
%4 = getelementptr inbounds %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %unwind_ex, i32 0, i32 1, !dbg !38
store i64 0, i64* %4, !dbg !38
%5 = ptrtoint %String* %str to i64, !dbg !38
%6 = getelementptr inbounds %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %unwind_ex, i32 0, i32 4, !dbg !38
store i64 %5, i64* %6, !dbg !38
%7 = getelementptr inbounds %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %unwind_ex, i32 0, i32 5, !dbg !38
store i32 1, i32* %7, !dbg !38
call void @__crystal_raise(%"struct.LibUnwind::Exception"* %unwind_ex), !dbg !39
unreachable, !dbg !39
}
; Function Attrs: uwtable
define internal %"struct.LibUnwind::Exception" @"*struct.LibUnwind::Exception::new:struct.LibUnwind::Exception"() #0 {
alloca:
%x = alloca %"struct.LibUnwind::Exception"
%0 = alloca %"struct.LibUnwind::Exception"
br label %entry
entry: ; preds = %alloca
%1 = bitcast %"struct.LibUnwind::Exception"* %0 to i8*
call void @llvm.memset.p0i8.i32(i8* %1, i8 0, i32 ptrtoint (%"struct.LibUnwind::Exception"* getelementptr (%"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* null, i32 1) to i32), i32 4, i1 false)
%2 = load %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %0
store %"struct.LibUnwind::Exception" %2, %"struct.LibUnwind::Exception"* %x
%3 = load %"struct.LibUnwind::Exception", %"struct.LibUnwind::Exception"* %x
ret %"struct.LibUnwind::Exception" %3
}
; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i32(i8* nocapture writeonly, i8, i32, i32, i1) #2
define internal i32* @"~LibUnwind::ReasonCode::CONTINUE_UNWIND:read"() {
entry:
%0 = load i1, i1* @"LibUnwind::ReasonCode::CONTINUE_UNWIND:init", !dbg !20
br i1 %0, label %initialized, label %not_initialized, !dbg !20
initialized: ; preds = %not_initialized, %entry
ret i32* @"LibUnwind::ReasonCode::CONTINUE_UNWIND", !dbg !20
not_initialized: ; preds = %entry
store i1 true, i1* @"LibUnwind::ReasonCode::CONTINUE_UNWIND:init", !dbg !20
call void @"~LibUnwind::ReasonCode::CONTINUE_UNWIND:init"(), !dbg !20
br label %initialized, !dbg !20
}
define internal void @"~LibUnwind::ReasonCode::CONTINUE_UNWIND:init"() {
entry:
ret void, !dbg !20
}
declare i32 @_Unwind_RaiseException(%"struct.LibUnwind::Exception"*)
; Function Attrs: noreturn
declare void @exit(i32) #3
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #4
attributes #0 = { uwtable }
attributes #1 = { noreturn uwtable }
attributes #2 = { argmemonly nounwind }
attributes #3 = { noreturn }
attributes #4 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3}
!0 = distinct !DICompileUnit(language: 32770, file: !1, producer: "Crystal", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "main_module", directory: ".")
!2 = !{}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = distinct !DISubprogram(name: "__crystal_main", linkageName: "__crystal_main", scope: !5, file: !5, type: !6, isLocal: true, isDefinition: true, isOptimized: false, unit: !0, variables: !2)
!5 = !DIFile(filename: "??", directory: ".")
!6 = !DISubroutineType(types: !7)
!7 = !{!8}
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!9 = !DILocation(line: 1, column: 1, scope: !10)
!10 = distinct !DILexicalBlock(scope: !4, file: !11, line: 1, column: 1)
!11 = !DIFile(filename: "noprelude_exception.cr", directory: "/home/lesell_b/tests/crystal/noprelude_exception")
!12 = !DILocation(line: 45, column: 3, scope: !10)
!13 = !DILocation(line: 46, column: 15, scope: !10)
!14 = !DILocation(line: 46, column: 3, scope: !10)
!15 = !DILocation(line: 47, column: 3, scope: !10)
!16 = !DILocation(line: 49, column: 3, scope: !10)
!17 = !DILocation(line: 50, column: 15, scope: !10)
!18 = !DILocation(line: 50, column: 3, scope: !10)
!19 = distinct !DISubprogram(name: "__crystal_personality", linkageName: "__crystal_personality", scope: !11, file: !11, line: 23, type: !6, isLocal: true, isDefinition: true, scopeLine: 23, isOptimized: false, unit: !0, variables: !2)
!20 = !DILocation(line: 23, column: 1, scope: !19)
!21 = distinct !DISubprogram(name: "__crystal_raise", linkageName: "__crystal_raise", scope: !11, file: !11, line: 28, type: !6, isLocal: true, isDefinition: true, scopeLine: 28, isOptimized: false, unit: !0, variables: !2)
!22 = !DILocation(line: 28, column: 1, scope: !21)
!23 = !DILocation(line: 29, column: 9, scope: !21)
!24 = !DILocation(line: 29, column: 3, scope: !21)
!25 = !DILocation(line: 30, column: 15, scope: !21)
!26 = !DILocation(line: 30, column: 3, scope: !21)
!27 = !DILocation(line: 32, column: 3, scope: !21)
!28 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !29, file: !29, line: 8, type: !6, isLocal: true, isDefinition: true, scopeLine: 8, isOptimized: false, unit: !0, variables: !2)
!29 = !DIFile(filename: "empty.cr", directory: "/usr/lib/crystal")
!30 = !DILocation(line: 9, column: 3, scope: !28)
!31 = !DILocation(line: 11, column: 3, scope: !28)
!32 = distinct !DISubprogram(name: "to_unsafe", linkageName: "to_unsafe", scope: !11, file: !11, line: 18, type: !6, isLocal: true, isDefinition: true, scopeLine: 18, isOptimized: false, unit: !0, variables: !2)
!33 = !DILocation(line: 18, column: 3, scope: !32)
!34 = !DILocation(line: 20, column: 5, scope: !32)
!35 = distinct !DISubprogram(name: "raise", linkageName: "raise", scope: !11, file: !11, line: 35, type: !6, isLocal: true, isDefinition: true, scopeLine: 35, isOptimized: false, unit: !0, variables: !2)
!36 = !DILocation(line: 35, column: 1, scope: !35)
!37 = !DILocation(line: 36, column: 15, scope: !35)
!38 = !DILocation(line: 36, column: 3, scope: !35)
!39 = !DILocation(line: 41, column: 3, scope: !35)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment