Last active
March 9, 2024 17:59
-
-
Save bew/1a55c69fe7cff0007d5be39fe8752da5 to your computer and use it in GitHub Desktop.
Minimal noprelude exception example in Crystal, with the generated LLVM IR code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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