fspl/generator/expression.go

432 lines
13 KiB
Go

package generator
// import "fmt"
import "git.tebibyte.media/fspl/fspl/llvm"
import "git.tebibyte.media/fspl/fspl/entity"
import "git.tebibyte.media/fspl/fspl/analyzer"
func (this *generator) generateBlock (block *entity.Block, mode resultMode) (llvm.Value, bool, error) {
if len(block.Steps) == 0 { return nil, false, nil }
lastIndex := len(block.Steps) - 1
for _, step := range block.Steps[:lastIndex] {
_, _, err := this.generateExpressionAny(step)
if err != nil { return nil, false, err }
}
return this.generateExpression(block.Steps[lastIndex], mode)
}
func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) {
loopEntry := this.blockManager.topLoop()
var irValue llvm.Value
if brk.Value != nil {
var err error
var loc bool
thisMode := loopEntry.mode
if thisMode == resultModeAny && len(loopEntry.breakBlocks) > 0 {
// there have been previous break statements, so be
// consistent with the already decided result mode
loc := loopEntry.loc
if loc { thisMode = resultModeLoc
} else { thisMode = resultModeVal }
}
irValue, loc, err = this.generateExpression(brk.Value, loopEntry.mode)
if err != nil { return nil, err }
loopEntry.loc = loc
}
loopEntry.breakBlocks = append(loopEntry.breakBlocks, &llvm.Incoming {
X: irValue,
Predecessor: this.blockManager.Block,
})
return nil, nil
}
func (this *generator) generateReturn (ret *entity.Return) (llvm.Value, error) {
if ret.Value == nil {
this.blockManager.NewRet(nil)
} else {
value, err := this.generateExpressionVal(ret.Value)
if err != nil { return nil, err }
this.blockManager.NewRet(value)
}
return nil, nil
}
func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) (llvm.Value, bool, error) {
condition, err := this.generateExpressionVal(ifelse.Condition)
if err != nil { return nil, false, err }
previous := this.blockManager.Block
var trueBlock, falseBlock *llvm.Block
var tru, fals llvm.Value
var loc bool
var irIncomings []*llvm.Incoming
trueBlock = this.blockManager.newBlock()
exitBlock := this.blockManager.newBlock()
this.blockManager.Block = trueBlock
tru, loc, err = this.generateExpression(ifelse.True, mode)
if err != nil { return nil, false, err }
if !this.blockManager.Terminated() {
if tru != nil {
irIncomings = append(irIncomings, &llvm.Incoming {
X: tru,
Predecessor: this.blockManager.Block,
})
}
this.blockManager.NewBr(exitBlock)
}
if ifelse.False == nil {
// there is no false case
previous.NewCondBr(condition, trueBlock, exitBlock)
this.blockManager.Block = exitBlock
return nil, loc, nil
} else {
// there is a false case
falseBlock = this.blockManager.newBlock()
thisMode := mode
if mode != resultModeAny {
if loc { thisMode = resultModeLoc
} else { thisMode = resultModeVal }
}
fals, _, err = this.generateExpression(ifelse.False, thisMode)
if err != nil { return nil, false, err }
if !this.blockManager.Terminated() {
if fals != nil {
irIncomings = append(irIncomings, &llvm.Incoming {
X: fals,
Predecessor: this.blockManager.Block,
})
}
this.blockManager.NewBr(exitBlock)
}
if mode == resultModeAny {
// discard results of statements
previous.NewCondBr(condition, trueBlock, falseBlock)
this.blockManager.Block = exitBlock
return nil, false, nil
} else {
// obtain results of statements
// set up phi to capture results
previous.NewCondBr(condition, trueBlock, falseBlock)
this.blockManager.Block = exitBlock
return this.blockManager.NewPhi(irIncomings...), loc, nil
}
}
}
func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llvm.Value, bool, error) {
value, err := this.generateExpressionLoc(match.Value)
if err != nil { return nil, false, err }
irUnionType, err := this.generateType(match.Value.Type())
if err != nil { return nil, false, err }
previousBlock := this.blockManager.Block
exitBlock := this.blockManager.newBlock()
irHashType := llvm.I64
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()
hash := match.CaseOrder[index]
irCases[index] = &llvm.Case {
X: llvm.NewConstInt(irHashType, int64(hash.Number())),
Target: caseBlock,
}
// create casted variable
irType, err := this.generateType(cas.Declaration.Type())
if err != nil { return nil, false, err }
dataFieldLoc := this.getUnionDataFieldLoc(value, irUnionType)
data := this.blockManager.NewLoad(irType, dataFieldLoc)
this.blockManager.addDeclaration(cas.Declaration, data)
// generate case expression
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 }
}
// create switch branch
this.blockManager.Block = previousBlock
hashFieldLoc := this.getUnionTypeFieldLoc(value, irUnionType)
hash := this.blockManager.NewLoad(irHashType, hashFieldLoc)
if defaultBlock == nil {
this.blockManager.NewSwitch(hash, exitBlock, irCases...)
} else {
this.blockManager.NewSwitch(hash, defaultBlock, irCases...)
}
// discard/obtain results
this.blockManager.Block = exitBlock
if mode == resultModeAny {
return nil, false, nil
} else {
irType, err := this.generateType(match.Type())
if err != nil { return nil, false, err }
if defaultBlock == nil {
irIncomings = append(irIncomings, &llvm.Incoming {
X: llvm.NewConstZeroInitializer(irType),
Predecessor: previousBlock,
})
}
return this.blockManager.NewPhi(irIncomings...), loc, nil
}
}
func (this *generator) generateSwitch (match *entity.Switch, mode resultMode) (llvm.Value, bool, error) {
irValue, err := this.generateExpressionVal(match.Value)
if err != nil { return nil, false, err }
irValueType, err := this.generateType(match.Value.Type())
if err != nil { return nil, false, err }
previousBlock := this.blockManager.Block
exitBlock := this.blockManager.newBlock()
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 value cases
for index, cas := range match.Cases {
caseBlock := this.blockManager.newBlock()
key := match.CaseOrder[index]
irCases[index] = &llvm.Case {
X: llvm.NewConstInt (
llvm.ReduceToBase(irValueType).(*llvm.TypeInt),
key),
Target: caseBlock,
}
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 }
}
// create switch branch
this.blockManager.Block = previousBlock
if defaultBlock == nil {
this.blockManager.NewSwitch(irValue, exitBlock, irCases...)
} else {
this.blockManager.NewSwitch(irValue, defaultBlock, irCases...)
}
// discard/obtain results
this.blockManager.Block = exitBlock
if mode == resultModeAny {
return nil, false, nil
} else {
irType, err := this.generateType(match.Type())
if err != nil { return nil, false, err }
if defaultBlock == nil {
irIncomings = append(irIncomings, &llvm.Incoming {
X: llvm.NewConstZeroInitializer(irType),
Predecessor: previousBlock,
})
}
return this.blockManager.NewPhi(irIncomings...), loc, nil
}
}
func (this *generator) generateLoop (loop *entity.Loop, mode resultMode) (llvm.Value, bool, error) {
irResultType, err := this.generateType(loop.Type())
if err != nil { return nil, false, err }
previous := this.blockManager.Block
body := this.blockManager.newBlock()
exit := this.blockManager.newBlock()
previous.NewBr(body)
loopEntry := this.blockManager.pushLoop(mode)
defer this.blockManager.popLoop()
this.blockManager.Block = body
_, _, err = this.generateExpressionAny(loop.Body)
if err != nil { return nil, false, err }
final := this.blockManager.Block
// discard/obtain results
this.blockManager.Block = exit
var irValue llvm.Value
if _, void := irResultType.(*llvm.TypeVoid); void {
loopEntry.tieUp(exit)
} else {
irValue = loopEntry.tieUpPhi(irResultType, exit)
}
if !final.Terminated () {
final.NewBr(body)
}
return irValue, loopEntry.loc, nil
}
func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Value, bool, error) {
irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, false, err }
irResultType, err := this.generateType(loop.Type())
if err != nil { return nil, false, err }
// element index
var irIndexLoc llvm.Value
if loop.Index != nil {
irIndexLoc, err = this.blockManager.addDeclaration (
loop.Index, llvm.NewConstZeroInitializer(irIndexType))
if err != nil { return nil, false, err }
} else {
irIndexLoc = this.blockManager.newAllocaFront(irIndexType)
}
// element value
irElementLoc, err := this.blockManager.addDeclaration(loop.Element, nil)
if err != nil { return nil, false, err }
irElementType, err := this.generateType(loop.Element.Type())
if err != nil { return nil, false, err }
// source slice/array
irOverLoc, err := this.generateExpressionLoc(loop.Over)
if err != nil { return nil, false, err }
irOverType, err := this.generateType(loop.Over.Type())
if err != nil { return nil, false, err }
baseOverType := analyzer.ReduceToBase(loop.Over.Type())
array, isArray := baseOverType.(*entity.TypeArray)
previous := this.blockManager.Block
header := this.blockManager.newBlock()
body := this.blockManager.newBlock()
exit := this.blockManager.newBlock()
previous.NewBr(header)
loopEntry := this.blockManager.pushLoop(mode)
defer this.blockManager.popLoop()
// check bounds
this.blockManager.Block = header
var irLength llvm.Value
if isArray {
irLength = llvm.NewConstInt(irIndexType, int64(array.Length))
} else {
irLength = this.blockManager.NewLoad (
irIndexType,
this.getSliceLengthFieldLoc(irOverLoc, irOverType))
}
this.blockManager.NewCondBr (
this.blockManager.NewICmp (
llvm.IPredicateULT,
this.blockManager.NewLoad(irIndexType, irIndexLoc),
irLength),
body, exit)
// set element
this.blockManager.Block = body
var irDataLoc llvm.Value
if isArray {
irDataLoc = irOverLoc
} else {
irDataLoc = this.getSliceDataAddress(irOverLoc, irOverType)
}
irElementLocInData := this.blockManager.NewGetElementPtr (
irElementType, irDataLoc,
this.blockManager.NewLoad(irIndexType, irIndexLoc))
this.blockManager.NewStore (
this.blockManager.NewLoad(irElementType, irElementLocInData),
irElementLoc)
// generate loop body
this.blockManager.Block = body
_, _, err = this.generateExpressionAny(loop.Body)
if err != nil { return nil, false, err }
// increment index
this.blockManager.NewStore (
this.blockManager.NewAdd (
this.blockManager.NewLoad(irIndexType, irIndexLoc),
llvm.NewConstInt(irIndexType, 1)),
irIndexLoc)
// discard/obtain results
final := this.blockManager.Block
this.blockManager.Block = exit
var irValue llvm.Value
if _, void := irResultType.(*llvm.TypeVoid); void {
loopEntry.tieUp(exit)
} else {
irValue = loopEntry.tieUpPhi(irResultType, exit, &llvm.Incoming {
X: llvm.NewConstZeroInitializer(irResultType),
Predecessor: header,
})
}
// loop back around
if !final.Terminated () {
final.NewBr(header)
}
return irValue, loopEntry.loc, nil
}