// returnStmt lowers the given return statement to LLVM IR, emitting code to f. func (m *Module) returnStmt(f *Function, stmt *ast.ReturnStmt) { // Input: // int f() { // return 42; // <-- relevant line // } // Output: // ret i32 42 if stmt.Result == nil { term, err := instruction.NewRet(nil) if err != nil { panic(fmt.Sprintf("unable to create ret terminator; %v", err)) } f.curBlock.SetTerm(term) f.curBlock = nil return } result := m.expr(f, stmt.Result) // Implicit conversion. resultType := f.Sig().Result() result = m.convert(f, result, resultType) term, err := instruction.NewRet(result) if err != nil { panic(fmt.Sprintf("unable to create ret terminator; %v", err)) } f.curBlock.SetTerm(term) f.curBlock = nil }
// endBody finalizes the generation of the function body. func (f *Function) endBody() error { if block := f.curBlock; block != nil && block.Term() == nil { switch { case f.Function.Name() == "main": // From C11 spec $5.1.2.2.3. // // "If the return type of the main function is a type compatible with // int, a return from the initial call to the main function is // equivalent to calling the exit function with the value returned by // the main function as its argument; reaching the } that terminates // the main function returns a value of 0." result := f.Sig().Result() zero := constZero(result) term, err := instruction.NewRet(zero) if err != nil { panic(fmt.Sprintf("unable to create ret terminator; %v", err)) } block.SetTerm(term) default: // Add void return terminator to the current basic block, if a // terminator is missing. switch result := f.Sig().Result(); { case irtypes.IsVoid(result): term, err := instruction.NewRet(nil) if err != nil { panic(fmt.Sprintf("unable to create ret instruction; %v", err)) } block.SetTerm(term) default: // The semantic analysis checker guarantees that all branches of // non-void functions end with return statements. Therefore, if we // reach the current basic block doesn't have a terminator at the // end of the function body, it must be unreachable. term, err := instruction.NewUnreachable() if err != nil { panic(fmt.Sprintf("unable to create unreachable instruction; %v", err)) } block.SetTerm(term) } } } f.curBlock = nil if err := f.AssignIDs(); err != nil { return errutil.Err(err) } return nil }
// NewRetInst returns a new return instruction based on the given result type // and value. func NewRetInst(typ, val interface{}) (*instruction.Ret, error) { if typ, ok := typ.(types.Type); ok { val, err := NewValue(typ, val) if err != nil { return nil, errutil.Err(err) } return instruction.NewRet(val) } return nil, errutil.Newf("invalid result type; expected types.Type, got %T", typ) }
// 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)) } }