// ifStmt lowers the given if statement to LLVM IR, emitting code to f. func (m *Module) ifStmt(f *Function, stmt *ast.IfStmt) { cond := m.cond(f, stmt.Cond) trueBranch := f.NewBasicBlock("") end := f.NewBasicBlock("") falseBranch := end if stmt.Else != nil { falseBranch = f.NewBasicBlock("") } term, err := instruction.NewBr(cond, trueBranch, falseBranch) if err != nil { panic(fmt.Sprintf("unable to create br terminator; %v", err)) } f.curBlock.SetTerm(term) f.curBlock = trueBranch m.stmt(f, stmt.Body) // Emit jump if body doesn't end with return statement (i.e. the current // basic block is none nil). if f.curBlock != nil { f.curBlock.emitJmp(end) } if stmt.Else != nil { f.curBlock = falseBranch m.stmt(f, stmt.Else) // Emit jump if body doesn't end with return statement (i.e. the current // basic block is none nil). if f.curBlock != nil { f.curBlock.emitJmp(end) } } f.curBlock = end }
// fixTerm replaces dummy values within the given terminator with their // corresponding local variables. func (m dummyMap) fixTerm(oldTerm instruction.Terminator) instruction.Terminator { switch oldTerm := oldTerm.(type) { case *instruction.Ret: oldVal := oldTerm.Value() var val value.Value if oldVal != nil { val = m.fixValue(oldVal) } term, err := instruction.NewRet(val) if err != nil { panic(errutil.Err(err)) } return term case *instruction.Jmp: target := m.fixNamedValue(oldTerm.Target()) term, err := instruction.NewJmp(target) if err != nil { panic(errutil.Err(err)) } return term case *instruction.Br: cond := m.fixValue(oldTerm.Cond()) trueBranch := m.fixNamedValue(oldTerm.TrueBranch()) falseBranch := m.fixNamedValue(oldTerm.FalseBranch()) term, err := instruction.NewBr(cond, trueBranch, falseBranch) if err != nil { panic(errutil.Err(err)) } return term case *instruction.Switch: panic("irx.dummyMap.fixTerm: Switch not yet implemented") case *instruction.IndirectBr: panic("irx.dummyMap.fixTerm: IndirectBr not yet implemented") case *instruction.Invoke: panic("irx.dummyMap.fixTerm: Invoke not yet implemented") case *instruction.Resume: panic("irx.dummyMap.fixTerm: Resume not yet implemented") case *instruction.CatchSwitch: panic("irx.dummyMap.fixTerm: CatchSwitch not yet implemented") case *instruction.CatchRet: panic("irx.dummyMap.fixTerm: CatchRet not yet implemented") case *instruction.CleanupRet: panic("irx.dummyMap.fixTerm: CleanupRet not yet implemented") case *instruction.Unreachable: panic("irx.dummyMap.fixTerm: Unreachable not yet implemented") default: panic(fmt.Sprintf("support for terminator type %T not yet implemented", oldTerm)) } }
// NewBrInst returns a new br instruction based on the given branching // condition, and the true and false target branches. func NewBrInst(condType, condVal, ltrueBranch, lfalseBranch interface{}) (*instruction.Br, error) { cond, err := NewValue(condType, condVal) if err != nil { return nil, errutil.Err(err) } falseBranch, ok := lfalseBranch.(*LocalDummy) if !ok { return nil, errutil.Newf("invalid false branch type; expected *LocalDummy, got %T", lfalseBranch) } trueBranch, ok := ltrueBranch.(*LocalDummy) if !ok { return nil, errutil.Newf("invalid true branch type; expected *LocalDummy, got %T", ltrueBranch) } return instruction.NewBr(cond, trueBranch, falseBranch) }
// whileStmt lowers the given while statement to LLVM IR, emitting code to f. func (m *Module) whileStmt(f *Function, stmt *ast.WhileStmt) { condBranch := f.NewBasicBlock("") f.curBlock.emitJmp(condBranch) f.curBlock = condBranch cond := m.cond(f, stmt.Cond) bodyBranch := f.NewBasicBlock("") endBranch := f.NewBasicBlock("") term, err := instruction.NewBr(cond, bodyBranch, endBranch) if err != nil { panic(fmt.Sprintf("unable to create br terminator; %v", err)) } f.curBlock.SetTerm(term) f.curBlock = bodyBranch m.stmt(f, stmt.Body) // Emit jump if body doesn't end with return statement (i.e. the current // basic block is none nil). if f.curBlock != nil { f.curBlock.emitJmp(condBranch) } f.curBlock = endBranch }
// binaryExpr lowers the given binary expression to LLVM IR, emitting code to f. func (m *Module) binaryExpr(f *Function, n *ast.BinaryExpr) value.Value { switch n.Op { // + case token.Add: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) addInst, err := instruction.NewAdd(x, y) if err != nil { panic(fmt.Sprintf("unable to create add instruction; %v", err)) } // Emit add instruction. return f.emitInst(addInst) // - case token.Sub: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) subInst, err := instruction.NewSub(x, y) if err != nil { panic(fmt.Sprintf("unable to create sub instruction; %v", err)) } // Emit sub instruction. return f.emitInst(subInst) // * case token.Mul: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) mulInst, err := instruction.NewMul(x, y) if err != nil { panic(fmt.Sprintf("unable to create mul instruction; %v", err)) } // Emit mul instruction. return f.emitInst(mulInst) // / case token.Div: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) // TODO: Add support for unsigned division. sdivInst, err := instruction.NewSDiv(x, y) if err != nil { panic(fmt.Sprintf("unable to create sdiv instruction; %v", err)) } // Emit sdiv instruction. return f.emitInst(sdivInst) // < case token.Lt: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) icmpInst, err := instruction.NewICmp(instruction.ICondSLT, x, y) if err != nil { panic(fmt.Sprintf("unable to create icmp instruction; %v", err)) } // Emit icmp instruction. return f.emitInst(icmpInst) // > case token.Gt: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) icmpInst, err := instruction.NewICmp(instruction.ICondSGT, x, y) if err != nil { panic(fmt.Sprintf("unable to create icmp instruction; %v", err)) } // Emit icmp instruction. return f.emitInst(icmpInst) // <= case token.Le: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) icmpInst, err := instruction.NewICmp(instruction.ICondSLE, x, y) if err != nil { panic(fmt.Sprintf("unable to create icmp instruction; %v", err)) } // Emit icmp instruction. return f.emitInst(icmpInst) // >= case token.Ge: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) icmpInst, err := instruction.NewICmp(instruction.ICondSGE, x, y) if err != nil { panic(fmt.Sprintf("unable to create icmp instruction; %v", err)) } // Emit icmp instruction. return f.emitInst(icmpInst) // != case token.Ne: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) icmpInst, err := instruction.NewICmp(instruction.ICondNE, x, y) if err != nil { panic(fmt.Sprintf("unable to create icmp instruction; %v", err)) } // Emit icmp instruction. return f.emitInst(icmpInst) // == case token.Eq: x, y := m.expr(f, n.X), m.expr(f, n.Y) x, y = m.implicitConversion(f, x, y) icmpInst, err := instruction.NewICmp(instruction.ICondEQ, x, y) if err != nil { panic(fmt.Sprintf("unable to create icmp instruction; %v", err)) } // Emit icmp instruction. return f.emitInst(icmpInst) // && case token.Land: x := m.cond(f, n.X) start := f.curBlock trueBranch := f.NewBasicBlock("") end := f.NewBasicBlock("") term, err := instruction.NewBr(x, trueBranch, end) if err != nil { panic(fmt.Sprintf("unable to create br terminator; %v", err)) } f.curBlock.SetTerm(term) f.curBlock = trueBranch y := m.cond(f, n.Y) trueBranch.emitJmp(end) f.curBlock = end var incs []*instruction.Incoming zero := constZero(irtypes.I1) inc, err := instruction.NewIncoming(zero, start) if err != nil { panic(fmt.Sprintf("unable to create incoming value; %v", err)) } incs = append(incs, inc) inc, err = instruction.NewIncoming(y, trueBranch) if err != nil { panic(fmt.Sprintf("unable to create incoming value; %v", err)) } incs = append(incs, inc) phiInst, err := instruction.NewPHI(incs) if err != nil { panic(fmt.Sprintf("unable to create br terminator; %v", err)) } // Emit phi instruction. return f.emitInst(phiInst) // = case token.Assign: y := m.expr(f, n.Y) switch expr := n.X.(type) { case *ast.Ident: m.identDef(f, expr, y) case *ast.IndexExpr: m.indexExprDef(f, expr, y) default: panic(fmt.Sprintf("support for assignment to type %T not yet implemented", expr)) } return y default: panic(fmt.Sprintf("support for binary operator %v not yet implemented", n.Op)) } }