From 5dda7b2186ff3d9c255d972327e0ab638bb2f2a8 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 25 Mar 2024 22:34:24 -0400 Subject: [PATCH] Generate match statement default cases --- compiler/compiler_test.go | 10 +++ .../data/match-default-print/fspl.mod | 2 + .../data/match-default-print/main.fspl | 11 +++ generator/control-flow_test.go | 86 +++++++++++++++++++ generator/expression.go | 67 ++++++++++----- 5 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 compiler/test-data/data/match-default-print/fspl.mod create mode 100644 compiler/test-data/data/match-default-print/main.fspl diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 3812bbb..9370dde 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -82,6 +82,16 @@ testUnit (test, 0, )} +func TestMatchDefaultPrint (test *testing.T) { +dependencies := []string { + compileDependency(test, "io"), +} +testUnit (test, +"/test-data/data/match-default-print", dependencies, +"", "something else\n", +0, +)} + func TestReturnAssign (test *testing.T) { dependencies := []string { compileDependency(test, "io"), diff --git a/compiler/test-data/data/match-default-print/fspl.mod b/compiler/test-data/data/match-default-print/fspl.mod new file mode 100644 index 0000000..bd8d3c7 --- /dev/null +++ b/compiler/test-data/data/match-default-print/fspl.mod @@ -0,0 +1,2 @@ +'624d4557-5291-4ad7-9283-7c200b9c2942' ++ 'io' diff --git a/compiler/test-data/data/match-default-print/main.fspl b/compiler/test-data/data/match-default-print/main.fspl new file mode 100644 index 0000000..36fc3b8 --- /dev/null +++ b/compiler/test-data/data/match-default-print/main.fspl @@ -0,0 +1,11 @@ +[main]: I32 'main' = { + x:UInt = 7 + [matchToInt x] + 0 +} + +U: (| Int F64 UInt) +[matchToInt u:U] = match u + | u:Int io::[println 'Int'] + | u:F64 io::[println 'F64'] + * io::[println 'something else'] diff --git a/generator/control-flow_test.go b/generator/control-flow_test.go index 963c8f0..e21c736 100644 --- a/generator/control-flow_test.go +++ b/generator/control-flow_test.go @@ -340,6 +340,92 @@ U: (| Int F64 UInt) `) } +func TestMatchDefault (test *testing.T) { +testString (test, +`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 } +%"AAAAAAAAAAAAAAAAAAAAAA==::Index" = type i64 +%"AAAAAAAAAAAAAAAAAAAAAA==::String" = type { ptr, %"AAAAAAAAAAAAAAAAAAAAAA==::Index" } +define void @"0zNZN147MN2wzMAQ6NS2dQ==::matchToInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) { +0: + %1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U" + store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1 + %2 = alloca i64 + %3 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String" + %4 = alloca [3 x i8] + %5 = alloca double + %6 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String" + %7 = alloca [3 x i8] + %8 = alloca %"AAAAAAAAAAAAAAAAAAAAAA==::String" + %9 = alloca [3 x i8] + %10 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 0 + %11 = load i64, ptr %10 + switch i64 %11, label %31 [ + i64 7620767046192759206, label %13 + i64 9186060094042213285, label %22 + ] +12: + ret void +13: + %14 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1 + %15 = load i64, ptr %14 + store i64 %15, ptr %2 + %16 = getelementptr [3 x i8], ptr %4, i32 0, i32 0 + store i8 73, ptr %16 + %17 = getelementptr [3 x i8], ptr %4, i32 0, i32 1 + store i8 110, ptr %17 + %18 = getelementptr [3 x i8], ptr %4, i32 0, i32 2 + store i8 116, ptr %18 + %19 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3, i32 0, i32 0 + store ptr %4, ptr %19 + %20 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3, i32 0, i32 1 + store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %20 + %21 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %3 + call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %21) + br label %12 +22: + %23 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1 + %24 = load double, ptr %23 + store double %24, ptr %5 + %25 = getelementptr [3 x i8], ptr %7, i32 0, i32 0 + store i8 70, ptr %25 + %26 = getelementptr [3 x i8], ptr %7, i32 0, i32 1 + store i8 54, ptr %26 + %27 = getelementptr [3 x i8], ptr %7, i32 0, i32 2 + store i8 52, ptr %27 + %28 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6, i32 0, i32 0 + store ptr %7, ptr %28 + %29 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6, i32 0, i32 1 + store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %29 + %30 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %6 + call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %30) + br label %12 +31: + %32 = getelementptr [3 x i8], ptr %9, i32 0, i32 0 + store i8 105, ptr %32 + %33 = getelementptr [3 x i8], ptr %9, i32 0, i32 1 + store i8 100, ptr %33 + %34 = getelementptr [3 x i8], ptr %9, i32 0, i32 2 + store i8 107, ptr %34 + %35 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8, i32 0, i32 0 + store ptr %9, ptr %35 + %36 = getelementptr %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8, i32 0, i32 1 + store %"AAAAAAAAAAAAAAAAAAAAAA==::Index" 3, ptr %36 + %37 = load %"AAAAAAAAAAAAAAAAAAAAAA==::String", ptr %8 + call void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %37) + br label %12 +} +declare void @"0zNZN147MN2wzMAQ6NS2dQ==::print"(%"AAAAAAAAAAAAAAAAAAAAAA==::String" %str) +`, +` +U: (| Int F64 UInt) +[print str:String] +[matchToInt u:U] = match u + | u:Int [print 'Int'] + | u:F64 [print 'F64'] + * [print 'idk'] +`) +} + func TestIfElseReturnValueUsed (test *testing.T) { testString (test, `%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1 diff --git a/generator/expression.go b/generator/expression.go index 304e067..254efb7 100644 --- a/generator/expression.go +++ b/generator/expression.go @@ -122,7 +122,6 @@ func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) ( } func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llvm.Value, bool, error) { - var loc bool value, err := this.generateExpressionLoc(match.Value) if err != nil { return nil, false, err } irUnionType, err := this.generateType(match.Value.Type()) @@ -134,6 +133,31 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv irCases := make([]*llvm.Case, len(match.Cases)) irIncomings := []*llvm.Incoming { } + loc := false + first := true + + generateCaseExpression := func (expression entity.Expression) error { + thisMode := mode + if !first && mode != resultModeAny { + if loc { thisMode = resultModeLoc + } else { thisMode = resultModeVal } + } + result, thisLoc, err := this.generateExpression(expression, thisMode) + if err != nil { return err } + if first { loc = thisLoc } + if !this.blockManager.Terminated() { + if expression != nil { + irIncomings = append(irIncomings, &llvm.Incoming { + X: result, + Predecessor: this.blockManager.Block, + }) + } + this.blockManager.NewBr(exitBlock) + } + return nil + } + + // generate type cases for index, cas := range match.Cases { // set up ir case caseBlock := this.blockManager.newBlock() @@ -151,30 +175,27 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv this.blockManager.addDeclaration(cas.Declaration, data) // generate case expression - thisMode := mode - if index != 0 && mode != resultModeAny { - if loc { thisMode = resultModeLoc - } else { thisMode = resultModeVal } - } - caseExpression, thisLoc, err := this.generateExpression(cas.Expression, thisMode) + err = generateCaseExpression(cas.Expression) + if err != nil { return nil, false, err } + } + + // generate default case + var defaultBlock llvm.Value + if match.Default != nil { + defaultBlock = this.blockManager.newBlock() + err = generateCaseExpression(match.Default.Expression) if err != nil { return nil, false, err } - if index == 0 { loc = thisLoc } - if !this.blockManager.Terminated() { - if caseExpression != nil { - irIncomings = append(irIncomings, &llvm.Incoming { - X: caseExpression, - Predecessor: this.blockManager.Block, - }) - } - this.blockManager.NewBr(exitBlock) - } } // create switch branch this.blockManager.Block = previousBlock hashFieldLoc := this.getUnionTypeFieldLoc(value, irUnionType) hash := this.blockManager.NewLoad(irHashType, hashFieldLoc) - this.blockManager.NewSwitch(hash, exitBlock, irCases...) + if defaultBlock == nil { + this.blockManager.NewSwitch(hash, exitBlock, irCases...) + } else { + this.blockManager.NewSwitch(hash, defaultBlock, irCases...) + } // discard/obtain results this.blockManager.Block = exitBlock @@ -183,10 +204,12 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv } else { irType, err := this.generateType(match.Type()) if err != nil { return nil, false, err } - irIncomings = append(irIncomings, &llvm.Incoming { - X: llvm.NewConstZeroInitializer(irType), - Predecessor: previousBlock, - }) + if defaultBlock == nil { + irIncomings = append(irIncomings, &llvm.Incoming { + X: llvm.NewConstZeroInitializer(irType), + Predecessor: previousBlock, + }) + } return this.blockManager.NewPhi(irIncomings...), loc, nil } }