fspl/llvm/function.go

190 lines
4.0 KiB
Go

package llvm
import "fmt"
import "strings"
type Function struct {
FunctionName string
Signature *TypeFunction
Parameters []*Parameter
Blocks []*Block
AddressSpace AddressSpace
// TODO complete this
}
type Parameter struct {
Register
}
func (this *Parameter) LLString () string {
return this.String()
}
func NewParameter (name string, ty Type) *Parameter {
parameter := &Parameter { }
parameter.RegisterName = name
parameter.Ty = ty
return parameter
}
func (this *Function) NewBlock () *Block {
block := &Block {
Parent: this,
}
this.Blocks = append(this.Blocks, block)
return block
}
func (this *Function) LLString () string {
this.assignIDs()
buffer := &strings.Builder { }
if len(this.Blocks) == 0 {
// Function declaration.
buffer.WriteString("declare")
buffer.WriteString(this.headerString())
buffer.WriteString("\n")
return buffer.String()
} else {
// Function definition.
buffer.WriteString("define")
buffer.WriteString(this.headerString())
fmt.Fprintf(buffer, " %s", this.bodyString())
return buffer.String()
}
}
func (this *Function) Type () Type {
return Pointer
}
func (this *Function) Name () string {
return this.FunctionName
}
func (this *Function) Identifier () string {
return EncodeFunctionName(this.Name())
}
func (this *Function) SetName (name string) {
this.FunctionName = name
}
func (this *Function) String () string {
return fmt.Sprintf("ptr %v", this.Identifier())
}
func (this *Function) assignIDs () {
counter := 0
for _, block := range this.Blocks {
block.SetName(fmt.Sprint(counter))
counter ++
block.assignIDs(&counter)
}
}
func (this *Function) headerString () string {
buffer := &strings.Builder { }
fmt.Fprintf(buffer, " %v", this.Signature.Return)
fmt.Fprintf(buffer, " %v(", this.Identifier())
for index, param := range this.Parameters {
if index > 0 { buffer.WriteString(", ") }
buffer.WriteString(param.LLString())
}
if this.Signature.Variadic {
if len(this.Parameters) > 0 {
buffer.WriteString(", ")
}
buffer.WriteString("...")
}
buffer.WriteString(")")
if this.AddressSpace != 0 {
fmt.Fprintf(buffer, " %v", this.AddressSpace)
}
return buffer.String()
}
func (this *Function) bodyString () string {
buffer := &strings.Builder { }
buffer.WriteString("{\n")
for _, block := range this.Blocks {
fmt.Fprintf(buffer, "%s", block.LLString())
}
buffer.WriteString("}\n")
return buffer.String()
}
type Block struct {
BlockName string
Parent *Function
Instructions []Instruction
}
func (this *Block) LLString () string {
buffer := &strings.Builder { }
fmt.Fprintf(buffer, "%s:\n", this.Name())
for _, instruction := range this.Instructions {
fmt.Fprintf(buffer, "\t%s\n", instruction.LLString())
}
return buffer.String()
}
func (this *Block) Name () string {
return this.BlockName
}
func (this *Block) SetName (name string) {
this.BlockName = name
}
func (this *Block) Identifier () string {
return EncodeRegisterName(this.Name())
}
func (this *Block) String () string {
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
}
func (this *Block) Type () Type {
return Label
}
func (this *Block) AddInstruction (instruction Instruction) {
if this.Terminated() {
panic("block is already terminated")
}
this.Instructions = append(this.Instructions, instruction)
}
func (this *Block) Terminated () bool {
if len(this.Instructions) == 0 { return false }
_, ok := this.Instructions[len(this.Instructions) - 1].(Terminator)
return ok
}
func (this *Block) SetTerminator (terminator Terminator) {
if this.Terminated() {
this.Instructions[len(this.Instructions) - 1] = terminator
} else {
this.AddInstruction(terminator)
}
}
func (this *Block) assignIDs (counter *int) {
for _, instruction := range this.Instructions {
if instruction, ok := instruction.(ValueInstruction); ok {
if !isVoid(instruction) {
instruction.SetName(fmt.Sprint(*counter))
*counter ++
}
}
}
}
func isVoid (instruction ValueInstruction) bool {
_, ok := instruction.Type().(*TypeVoid)
return ok
}