Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
dist/
dist/
example/ffi.go
example/ffi_go.flint
8 changes: 8 additions & 0 deletions example/main.flint
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn test(i: Int) Nil {
print(i)
if i < 10 then test(i + 1) else print(1)
}

pub fn main() Nil {
test(1)
}
12 changes: 11 additions & 1 deletion example/match.flint
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
@external(c, "flint_stdlib", "print")
pub fn print(string: String) Nil

fn test(i: Int) String {
match i {
| 1 -> "Hello"
| _ -> match i == 10 {
| True -> "true"
| False -> "false"
}
}
}

pub fn main() Nil {
print("Hello\n")
print(test(3))
}
6 changes: 3 additions & 3 deletions internal/cli/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func loadAndParse(filename string) (*parser.Program, *typechecker.TypeChecker) {
}
}

// for _, ex := range prog.Exprs {
// fmt.Println(parser.DumpExpr(ex))
// }
for _, ex := range prog.Exprs {
fmt.Println(parser.DumpExpr(ex))
}
return prog, tc
}
133 changes: 18 additions & 115 deletions internal/codegen/codegen.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package codegen

import (
"fmt"

"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
"github.com/llir/llvm/ir/enum"
"github.com/llir/llvm/ir/types"
"github.com/llir/llvm/ir/value"

"flint/internal/lexer"
"flint/internal/parser"
)

Expand Down Expand Up @@ -242,106 +241,6 @@ func (cg *CodeGen) emitMatchBody(b *ir.Block, body parser.Expr, isTail bool) val
}
}

func (cg *CodeGen) emitMatch(b *ir.Block, m *parser.MatchExpr, isTail bool) value.Value {
parent := b.Parent
matchId := cg.globalMatchCount
cg.globalMatchCount++

scrutinee := cg.emitExpr(b, m.Value, false)
var incomings []*ir.Incoming
var phiType types.Type
current := b

blockMap := make(map[string]*ir.Block)
bodyMap := make(map[string]value.Value, 0)

for idx, arm := range m.Arms {
armName := fmt.Sprintf("match.%d.arm.%d", matchId, idx)
nextName := fmt.Sprintf("match.%d.next.%d", matchId, idx)

armBlock := parent.NewBlock(armName)
nextBlock := parent.NewBlock(nextName)

cond := cg.emitMatchCond(current, scrutinee, arm.Pattern, arm.Guard)
current.NewCondBr(cond, armBlock, nextBlock)

blockMap[armName] = armBlock
blockMap[nextName] = nextBlock

val := cg.emitMatchBody(armBlock, arm.Body, isTail)
bodyMap[armName] = val

pred := armBlock
if val != nil {
if pb := parentBlockOfValue(val); pb != nil {
pred = pb
}
}

if val != nil {
if phiType == nil {
phiType = val.Type()
} else if !val.Type().Equal(phiType) {
panic(fmt.Sprintf("match arm type mismatch: %v vs %v (arm %d)", val.Type(), phiType, idx))
}
incomings = append(incomings, &ir.Incoming{
X: val,
Pred: pred,
})
} else if phiType != nil {
incomings = append(incomings, &ir.Incoming{
X: constant.NewUndef(phiType),
Pred: pred,
})
blocks := make([]*ir.Block, 0)
for _, v := range parent.Blocks {
if v.Name() != armName {
blocks = append(blocks, v)
}
}
}
current = nextBlock
}

mergeBlock := parent.NewBlock(fmt.Sprintf("match.%d.merge", matchId))

for idx := range m.Arms {
armName := fmt.Sprintf("match.%d.arm.%d", matchId, idx)
armBlock := blockMap[armName]
val := bodyMap[armName]

pred := armBlock
if val != nil {
if pb := parentBlockOfValue(val); pb != nil {
pred = pb
}
}

if pred.Term == nil {
pred.NewBr(mergeBlock)
}
}

if current.Term == nil {
current.NewBr(mergeBlock)
if phiType != nil {
incomings = append(incomings, &ir.Incoming{
X: constant.NewUndef(phiType),
Pred: current,
})
}
}
if phiType == nil {
mergeBlock.NewRet(nil)
return nil
}
phi := mergeBlock.NewPhi(incomings...)
if isTail {
mergeBlock.NewRet(phi)
}
return phi
}

func (cg *CodeGen) emitMatchCond(b *ir.Block, scr value.Value, pat parser.Expr, guard parser.Expr) value.Value {
var baseCond value.Value
switch p := pat.(type) {
Expand All @@ -364,26 +263,30 @@ func (cg *CodeGen) emitMatchCond(b *ir.Block, scr value.Value, pat parser.Expr,
lit := constant.NewInt(types.I1, intVal)
baseCond = b.NewICmp(enum.IPredEQ, scr, lit)
case *parser.Identifier:
if p.Name == "_" {
if p.Pos.Kind == lexer.Underscore {
baseCond = constant.True
} else {
cg.locals[p.Name] = scr
// alloc := b.NewAlloca(scr.Type())
// b.NewStore(scr, alloc)
// cg.locals[p.Name] = alloc
baseCond = constant.True
}
default:
panic("unsupported match pattern: " + pat.NodeType())
}
if guard != nil {
guardVal := cg.emitExpr(b, guard, false)
if _, ok := guardVal.Type().(*types.IntType); ok {
if guardVal.Type() != types.I1 {
guardVal = b.NewTrunc(guardVal, types.I1)
}
} else {
panic("guard expression is not a boolean")
}
baseCond = b.NewAnd(baseCond, guardVal)
}
// disable guards for now

// if guard != nil {
// guardVal := cg.emitExpr(b, guard, false)
// if _, ok := guardVal.Type().(*types.IntType); ok {
// if guardVal.Type() != types.I1 {
// guardVal = b.NewTrunc(guardVal, types.I1)
// }
// } else {
// panic("guard expression is not a boolean")
// }
// baseCond = b.NewAnd(baseCond, guardVal)
// }
return baseCond
}

Expand Down
6 changes: 5 additions & 1 deletion internal/codegen/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package codegen

import (
"flint/internal/parser"
"reflect"

"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
Expand Down Expand Up @@ -31,7 +32,10 @@ func (cg *CodeGen) emitExpr(b *ir.Block, e parser.Expr, isTail bool) value.Value
if ptr == nil {
panic("undefined variable: " + v.Name)
}
return b.NewLoad(ptr.Type().(*types.PointerType).ElemType, ptr)
if reflect.TypeOf(ptr.Type()).String() == "*types.PointerType" {
return b.NewLoad(ptr.Type().(*types.PointerType).ElemType, ptr)
}
return b.NewLoad(ptr.Type(), ptr)
case *parser.InfixExpr:
return cg.emitInfix(b, v)
case *parser.IfExpr:
Expand Down
149 changes: 149 additions & 0 deletions internal/codegen/match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package codegen

import (
"flint/internal/parser"
"fmt"
"reflect"

"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
"github.com/llir/llvm/ir/types"
"github.com/llir/llvm/ir/value"
)

func (cg *CodeGen) emitMatch(b *ir.Block, m *parser.MatchExpr, isTail bool) value.Value {
parent := b.Parent

matchId := cg.globalMatchCount
cg.globalMatchCount++

armMap := make(map[string]*parser.MatchArm)
armBlockMap := make(map[string]*ir.Block)
armCheckMap := make(map[string]*ir.Block)
armBodyMap := make(map[string]value.Value)
checkList := make([]*ir.Block, 0)
nextList := make([]*ir.Block, 0)

checkList = append(checkList, b)

// wildCardName := fmt.Sprintf("match.%d.wild", matchId)

for caseId, arm := range m.Arms {
var name string
// if arm.IsWildCardArm() {
// name = wildCardName
// } else {
name = fmt.Sprintf("match.%d.arm.%d", matchId, caseId)
// }

armBlock := parent.NewBlock(name)
if caseId != 0 && !arm.IsWildCardArm() {
checkName := fmt.Sprintf("match.%d.check.%d", matchId, caseId)
checkList = append(checkList, parent.NewBlock(checkName))
nextList = append(nextList, checkList[len(checkList)-1])
armCheckMap[checkName] = checkList[len(checkList)-1]
} else if caseId != 0 {
checkList = append(checkList, nil)
nextList = append(nextList, armBlock)
}

armMap[name] = arm
armBlockMap[name] = armBlock
armBodyMap[name] = cg.emitMatchBody(armBlock, arm.Body, isTail)
}

scrutinee := cg.emitExpr(b, m.Value, false)
var incomings []*ir.Incoming
var phiType types.Type
mergeBlock := parent.NewBlock(fmt.Sprintf("match.%d", matchId))

nextList = append(nextList, mergeBlock)

current := b
armId := 0
for name, arm := range armMap {
armBlock := armBlockMap[name]
armBody := armBodyMap[name]

if armBody != nil {
if phiType == nil {
phiType = armBody.Type()
} else if !armBody.Type().Equal(phiType) {
panic(fmt.Sprintf("match arm type mismatch: %v vs %v (arm %d)", armBody.Type(), phiType, armId))
}
}

if !armMap[name].IsWildCardArm() {
current = checkList[armId]

if current != nil {
cond := cg.emitMatchCond(current, scrutinee, arm.Pattern, arm.Guard)
current.NewCondBr(cond, armBlock, nextList[armId])
}
}

if armBlock.Term == nil {
armBlock.NewBr(mergeBlock)

if armBody != nil {
if phiType == nil {
phiType = armBody.Type()
} else if !armBody.Type().Equal(phiType) {
panic(fmt.Sprintf("match arm type mismatch: %v vs %v (arm %d)", armBody.Type(), phiType, armId))
}
incomings = append(incomings, &ir.Incoming{
X: armBody,
Pred: armBlock,
})
} else if phiType != nil {
incomings = append(incomings, &ir.Incoming{
X: constant.NewUndef(phiType),
Pred: armBlock,
})
}
}

armId++
}
if phiType == nil {
mergeBlock.NewRet(nil)
return nil
}

for _, v := range checkList {
if v != nil {
opLen := len(b.Term.Operands())
for i := range opLen {
op := *v.Term.Operands()[i]
if reflect.TypeOf(op) == reflect.TypeOf(mergeBlock) {
if op.(*ir.Block) == mergeBlock {
incomings = append(incomings, ir.NewIncoming(
constant.NewUndef(phiType),
v,
))
}
}
}
}
}

opLen := len(b.Term.Operands())
for i := range opLen {
op := *b.Term.Operands()[i]
if reflect.TypeOf(op) == reflect.TypeOf(mergeBlock) {
if op.(*ir.Block) == mergeBlock {
incomings = append(incomings, ir.NewIncoming(
constant.NewUndef(phiType),
b,
))
}
}
}

phi := mergeBlock.NewPhi(incomings...)
if isTail {
mergeBlock.NewRet(phi)
}
return phi

}
4 changes: 4 additions & 0 deletions internal/parser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ func (m *MatchArm) NodeType() string {
return "MatchArm"
}

func (m *MatchArm) IsWildCardArm() bool {
return m.Pattern.NodeType() == "Identifier" && m.Pattern.(*Identifier).Name == "_"
}

type MatchExpr struct {
Value Expr
Arms []*MatchArm
Expand Down