github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/pogo/instructions.go (about) 1 // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors 2 // Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package pogo 6 7 import ( 8 "fmt" 9 "go/ast" 10 "go/types" 11 "reflect" 12 13 "golang.org/x/tools/go/ssa" 14 15 "github.com/tardisgo/tardisgo/tgoutil" 16 ) 17 18 // RegisterName returns the name of an ssa.Value, a utility function in case it needs to be altered. 19 func (comp *Compilation) RegisterName(val ssa.Value) string { 20 //NOTE the SSA code says that name() should not be relied on, so this code may need to alter 21 return LanguageList[comp.TargetLang].RegisterName(val) 22 } 23 24 // Handle an individual instruction. 25 func (comp *Compilation) emitInstruction(instruction interface{}, operands []*ssa.Value) (emitPhiFlag bool) { 26 l := comp.TargetLang 27 emitPhiFlag = true 28 errorInfo := "" 29 _, isDebug := instruction.(*ssa.DebugRef) 30 if !isDebug { // Don't update the code position for debug refs 31 prev := comp.LatestValidPosHash 32 comp.MakePosHash(instruction.(ssa.Instruction).Pos()) // this so that we log the nearby position info 33 if prev != comp.LatestValidPosHash { // new info, so put out an update 34 if comp.DebugFlag { // but only in Debug mode 35 fmt.Fprintln(&LanguageList[l].buffer, 36 LanguageList[l].SetPosHash()) 37 } 38 } 39 errorInfo = comp.CodePosition(instruction.(ssa.Instruction).Pos()) 40 } 41 if errorInfo == "" { 42 errorInfo = comp.previousErrorInfo 43 } else { 44 comp.previousErrorInfo = "near " + errorInfo 45 errorInfo = "@ " + errorInfo 46 } 47 errorInfo = reflect.TypeOf(instruction).String() + " " + errorInfo //TODO consider removing as for DEBUG only 48 instrVal, hasVal := instruction.(ssa.Value) 49 register := "" 50 comment := "" 51 if hasVal { 52 register = comp.RegisterName(instrVal) 53 comment = fmt.Sprintf("%s = %+v %s", register, instruction, errorInfo) 54 //emitComment(comment) 55 switch len(*instruction.(ssa.Value).Referrers()) { 56 case 0: // no other instruction uses the result of this one 57 comment += " [REGISTER VALUE UN-USED]" 58 register = "" 59 case 1: // only 1 other use of the register 60 // TODO register optimisation currently disabled, consider reimplimentation 61 user := (*instruction.(ssa.Value).Referrers())[0] 62 if user.Block() == instruction.(ssa.Instruction).Block() { 63 comment += " [REGISTER MAY BE OPTIMIZABLE]" 64 } 65 default: //multiple usage of the register 66 } 67 if len(register) > 0 { 68 if LanguageList[comp.TargetLang].LangType(instruction.(ssa.Value).Type(), false, errorInfo) == "" { // NOTE an empty type def makes a register useless too 69 register = "" 70 } 71 } 72 } else { 73 comment = fmt.Sprintf("%+v %s", instruction, errorInfo) 74 //emitComment(comment) 75 } 76 switch instruction.(type) { 77 case *ssa.Jump: 78 fmt.Fprintln(&LanguageList[l].buffer, 79 LanguageList[l].Jump(instruction.(*ssa.Jump).Block().Succs[0].Index, 80 instruction.(*ssa.Jump).Block().Index, 81 LanguageList[l].PhiCode(false, instruction.(*ssa.Jump).Block().Index, 82 instruction.(*ssa.Jump).Block().Succs[0].Instrs, 83 errorInfo))+ 84 LanguageList[l].Comment(comment)) 85 86 case *ssa.If: 87 fmt.Fprintln(&LanguageList[l].buffer, 88 LanguageList[l].If(*operands[0], 89 instruction.(*ssa.If).Block().Succs[0].Index, 90 instruction.(*ssa.If).Block().Succs[1].Index, 91 instruction.(*ssa.If).Block().Index, 92 LanguageList[l].PhiCode(false, instruction.(*ssa.If).Block().Index, 93 instruction.(*ssa.If).Block().Succs[0].Instrs, errorInfo), 94 LanguageList[l].PhiCode(false, instruction.(*ssa.If).Block().Index, 95 instruction.(*ssa.If).Block().Succs[1].Instrs, errorInfo), 96 errorInfo)+LanguageList[l].Comment(comment)) 97 98 case *ssa.Phi: 99 text := "" 100 if len(*instruction.(*ssa.Phi).Referrers()) > 0 { 101 phiEntries := make([]int, len(operands)) 102 valEntries := make([]interface{}, len(operands)) 103 for o := range operands { 104 phiEntries[o] = instruction.(*ssa.Phi).Block().Preds[o].Index 105 valEntries[o] = *operands[o] 106 } 107 text = LanguageList[l].Phi(register, phiEntries, valEntries, 108 LanguageList[l].LangType(instrVal.Type(), true, errorInfo), errorInfo) 109 } 110 fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment)) 111 112 case *ssa.Call: 113 if instruction.(*ssa.Call).Call.IsInvoke() { 114 fmt.Fprintln(&LanguageList[l].buffer, 115 LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Call).Parent()), 116 false, false, comp.grMap[instruction.(*ssa.Call).Parent()], 117 instruction.(*ssa.Call).Call, errorInfo)+LanguageList[l].Comment(comment)) 118 } else { 119 switch instruction.(*ssa.Call).Call.Value.(type) { 120 case *ssa.Builtin: 121 comp.emitCall(true, false, false, comp.grMap[instruction.(*ssa.Call).Parent()], 122 register, instruction.(*ssa.Call).Call, errorInfo, comment) 123 default: 124 comp.emitCall(false, false, false, comp.grMap[instruction.(*ssa.Call).Parent()], 125 register, instruction.(*ssa.Call).Call, errorInfo, comment) 126 } 127 } 128 129 case *ssa.Go: 130 if instruction.(*ssa.Go).Call.IsInvoke() { 131 if comp.grMap[instruction.(*ssa.Go).Parent()] != true { 132 panic("attempt to Go a method, from a function that does not use goroutines at " + errorInfo) 133 } 134 fmt.Fprintln(&LanguageList[l].buffer, 135 LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Go).Parent()), 136 true, false, true, instruction.(*ssa.Go).Call, errorInfo)+ 137 LanguageList[l].Comment(comment)) 138 } else { 139 switch instruction.(*ssa.Go).Call.Value.(type) { 140 case *ssa.Builtin: // no builtin functions can be go'ed 141 comp.LogError(errorInfo, "pogo", fmt.Errorf("builtin functions cannot be go'ed")) 142 default: 143 if comp.grMap[instruction.(*ssa.Go).Parent()] != true { 144 panic("attempt to Go a function, from a function does not use goroutines at " + errorInfo) 145 } 146 comp.emitCall(false, true, false, true, 147 register, instruction.(*ssa.Go).Call, errorInfo, comment) 148 } 149 } 150 151 case *ssa.Defer: 152 if instruction.(*ssa.Defer).Call.IsInvoke() { 153 fmt.Fprintln(&LanguageList[l].buffer, 154 LanguageList[l].EmitInvoke(register, 155 getFnPath(instruction.(*ssa.Defer).Parent()), 156 false, true, comp.grMap[instruction.(*ssa.Defer).Parent()], 157 instruction.(*ssa.Defer).Call, errorInfo)+ 158 LanguageList[l].Comment(comment)) 159 } else { 160 switch instruction.(*ssa.Defer).Call.Value.(type) { 161 case *ssa.Builtin: // no builtin functions can be defer'ed - TODO: the spec does allow this in some circumstances 162 switch instruction.(*ssa.Defer).Call.Value.(*ssa.Builtin).Name() { 163 case "close": 164 //LogError(errorInfo, "pogo", fmt.Errorf("builtin function close() cannot be defer'ed")) 165 comp.emitCall(true, false, true, comp.grMap[instruction.(*ssa.Defer).Parent()], 166 register, instruction.(*ssa.Defer).Call, errorInfo, comment) 167 default: 168 comp.LogError(errorInfo, "pogo", fmt.Errorf("builtin functions cannot be defer'ed")) 169 } 170 default: 171 comp.emitCall(false, false, true, comp.grMap[instruction.(*ssa.Defer).Parent()], 172 register, instruction.(*ssa.Defer).Call, errorInfo, comment) 173 } 174 } 175 176 case *ssa.Return: 177 emitPhiFlag = false 178 r := LanguageList[l].Ret(operands, errorInfo) 179 fmt.Fprintln(&LanguageList[l].buffer, r+LanguageList[l].Comment(comment)) 180 181 case *ssa.Panic: 182 emitPhiFlag = false 183 fmt.Fprintln(&LanguageList[l].buffer, 184 LanguageList[l].Panic(*operands[0], errorInfo, 185 comp.grMap[instruction.(*ssa.Panic).Parent()])+LanguageList[l].Comment(comment)) 186 187 case *ssa.UnOp: 188 if register == "" && instruction.(*ssa.UnOp).Op.String() != "<-" { 189 comp.emitComment(comment) 190 } else { 191 fmt.Fprintln(&LanguageList[l].buffer, 192 LanguageList[l].UnOp(register, instrVal.Type(), instruction.(*ssa.UnOp).Op.String(), *operands[0], 193 instruction.(*ssa.UnOp).CommaOk, errorInfo)+ 194 LanguageList[l].Comment(comment)) 195 } 196 197 case *ssa.BinOp: 198 if register == "" { 199 comp.emitComment(comment) 200 } else { 201 op := instruction.(*ssa.BinOp).Op.String() 202 fmt.Fprintln(&LanguageList[l].buffer, 203 LanguageList[l].BinOp(register, instrVal.Type(), op, *operands[0], *operands[1], errorInfo)+ 204 LanguageList[l].Comment(comment)) 205 } 206 207 case *ssa.Store: 208 fmt.Fprintln(&LanguageList[l].buffer, 209 LanguageList[l].Store(*operands[0], *operands[1], errorInfo)+LanguageList[l].Comment(comment)) 210 211 case *ssa.Send: 212 fmt.Fprintln(&LanguageList[l].buffer, 213 LanguageList[l].Send(*operands[0], *operands[1], errorInfo)+LanguageList[l].Comment(comment)) 214 215 case *ssa.Convert: 216 fmt.Fprintln(&LanguageList[l].buffer, 217 LanguageList[l].Convert(register, LanguageList[l].LangType(instrVal.Type(), false, errorInfo), instrVal.Type(), *operands[0], errorInfo)+ 218 LanguageList[l].Comment(comment)) 219 220 case *ssa.ChangeType: 221 fmt.Fprintln(&LanguageList[l].buffer, 222 LanguageList[l].ChangeType(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+ 223 LanguageList[l].Comment(comment)) 224 225 case *ssa.MakeInterface: 226 fmt.Fprintln(&LanguageList[l].buffer, 227 LanguageList[l].MakeInterface(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+ 228 LanguageList[l].Comment(comment)) 229 230 case *ssa.ChangeInterface: 231 fmt.Fprintln(&LanguageList[l].buffer, 232 LanguageList[l].ChangeInterface(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+ 233 LanguageList[l].Comment(comment)) 234 235 case *ssa.TypeAssert: 236 fmt.Fprintln(&LanguageList[l].buffer, 237 LanguageList[l].TypeAssert(register, instruction.(*ssa.TypeAssert).X, 238 instruction.(*ssa.TypeAssert).AssertedType, instruction.(*ssa.TypeAssert).CommaOk, errorInfo)+ 239 LanguageList[l].Comment(comment)) 240 241 case *ssa.RunDefers: 242 fmt.Fprintln(&LanguageList[l].buffer, 243 LanguageList[l].RunDefers(comp.grMap[instruction.(*ssa.RunDefers).Parent()])+ 244 LanguageList[l].Comment(comment)) 245 246 case *ssa.Alloc: 247 fmt.Fprintln(&LanguageList[l].buffer, 248 LanguageList[l].Alloc(register, instruction.(*ssa.Alloc).Heap, 249 instruction.(*ssa.Alloc).Type(), errorInfo)+ 250 LanguageList[l].Comment(instruction.(*ssa.Alloc).Comment+" "+comment)) 251 252 case *ssa.MakeClosure: 253 fmt.Fprintln(&LanguageList[l].buffer, 254 LanguageList[l].MakeClosure(register, 255 instruction, 256 errorInfo)+ 257 LanguageList[l].Comment(comment)) 258 259 case *ssa.MakeSlice: 260 fmt.Fprintln(&LanguageList[l].buffer, 261 LanguageList[l].MakeSlice(register, 262 instruction, 263 errorInfo)+ 264 LanguageList[l].Comment(comment)) 265 266 case *ssa.MakeChan: 267 fmt.Fprintln(&LanguageList[l].buffer, 268 LanguageList[l].MakeChan(register, 269 instruction, 270 errorInfo)+ 271 LanguageList[l].Comment(comment)) 272 273 case *ssa.MakeMap: 274 fmt.Fprintln(&LanguageList[l].buffer, 275 LanguageList[l].MakeMap(register, 276 instruction, 277 errorInfo)+ 278 LanguageList[l].Comment(comment)) 279 280 case *ssa.MapUpdate: 281 fmt.Fprintln(&LanguageList[l].buffer, 282 LanguageList[l].MapUpdate(*operands[0], *operands[1], *operands[2], errorInfo)+LanguageList[l].Comment(comment)) 283 284 case *ssa.Range: 285 fmt.Fprintln(&LanguageList[l].buffer, 286 LanguageList[l].Range(register, *operands[0], errorInfo)+LanguageList[l].Comment(comment)) 287 288 case *ssa.Next: 289 fmt.Fprintln(&LanguageList[l].buffer, 290 LanguageList[l].Next(register, *operands[0], instruction.(*ssa.Next).IsString, 291 errorInfo)+LanguageList[l].Comment(comment)) 292 293 case *ssa.Lookup: 294 fmt.Fprintln(&LanguageList[l].buffer, 295 LanguageList[l].Lookup(register, *operands[0], *operands[1], instruction.(*ssa.Lookup).CommaOk, errorInfo)+ 296 LanguageList[l].Comment(comment)) 297 298 case *ssa.Extract: 299 if register == "" { // rquired here because of a "feature" in the generated SSA form 300 comp.emitComment(comment) 301 } else { 302 fmt.Fprintln(&LanguageList[l].buffer, 303 LanguageList[l].Extract(register, *operands[0], instruction.(*ssa.Extract).Index, errorInfo)+ 304 LanguageList[l].Comment(comment)) 305 } 306 307 case *ssa.Slice: 308 // TODO see http://tip.golang.org/doc/go1.2#three_index 309 // TODO add third parameter when SSA code provides it to enable slice instructions to specify a capacity 310 if register == "" { 311 comp.emitComment(comment) 312 } else { 313 fmt.Fprintln(&LanguageList[l].buffer, 314 LanguageList[l].Slice(register, instruction.(*ssa.Slice).X, 315 instruction.(*ssa.Slice).Low, instruction.(*ssa.Slice).High, errorInfo)+ 316 LanguageList[l].Comment(comment)) 317 318 } 319 320 case *ssa.Index: 321 if register == "" { 322 comp.emitComment(comment) 323 } else { 324 doRangeCheck := true 325 aLen := 0 326 switch instruction.(*ssa.Index).X.Type().(type) { 327 case *types.Array: 328 aLen = int(instruction.(*ssa.Index).X.Type().(*types.Array).Len()) 329 case *types.Pointer: 330 switch instruction.(*ssa.Index).X.Type().(*types.Pointer).Elem().(type) { 331 case *types.Array: 332 aLen = int(instruction.(*ssa.Index).X.Type().(*types.Pointer).Elem().(*types.Array).Len()) 333 } 334 } 335 if aLen > 0 { 336 _, indexIsConst := instruction.(*ssa.Index).Index.(*ssa.Const) 337 if indexIsConst { 338 // this error handling is defensive, as the Go SSA code catches this error 339 index := instruction.(*ssa.Index).Index.(*ssa.Const).Int64() 340 if (index < 0) || (index >= int64(aLen)) { 341 comp.LogError(errorInfo, "pogo", fmt.Errorf("index [%d] out of range: 0 to %d", index, aLen-1)) 342 } 343 doRangeCheck = false 344 } 345 } 346 if doRangeCheck { 347 fmt.Fprintln(&LanguageList[l].buffer, 348 LanguageList[l].RangeCheck(instruction.(*ssa.Index).X, instruction.(*ssa.Index).Index, aLen, errorInfo)) 349 } 350 fmt.Fprintln(&LanguageList[l].buffer, 351 LanguageList[l].Index(register, *operands[0], *operands[1], errorInfo)+ 352 LanguageList[l].Comment(comment)) 353 } 354 355 case *ssa.IndexAddr: 356 if register == "" { 357 comp.emitComment(comment) 358 } else { 359 doRangeCheck := true 360 aLen := 0 361 switch instruction.(*ssa.IndexAddr).X.Type().(type) { 362 case *types.Array: 363 aLen = int(instruction.(*ssa.IndexAddr).X.Type().(*types.Array).Len()) 364 case *types.Pointer: 365 switch instruction.(*ssa.IndexAddr).X.Type().(*types.Pointer).Elem().(type) { 366 case *types.Array: 367 aLen = int(instruction.(*ssa.IndexAddr).X.Type().(*types.Pointer).Elem().(*types.Array).Len()) 368 } 369 } 370 if aLen > 0 { 371 _, indexIsConst := instruction.(*ssa.IndexAddr).Index.(*ssa.Const) 372 if indexIsConst { 373 index := instruction.(*ssa.IndexAddr).Index.(*ssa.Const).Int64() 374 if (index < 0) || (index >= int64(aLen)) { 375 comp.LogError(errorInfo, "pogo", fmt.Errorf("index [%d] out of range: 0 to %d", index, aLen-1)) 376 } 377 doRangeCheck = false 378 } 379 } 380 if doRangeCheck { 381 fmt.Fprintln(&LanguageList[l].buffer, 382 LanguageList[l].RangeCheck(instruction.(*ssa.IndexAddr).X, instruction.(*ssa.IndexAddr).Index, aLen, errorInfo)+ 383 LanguageList[l].Comment(comment+" [POINTER]")) 384 } 385 fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].IndexAddr(register, instruction, errorInfo), 386 LanguageList[l].Comment(comment+" [POINTER]")) 387 388 } 389 390 case *ssa.FieldAddr: 391 fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].FieldAddr(register, instruction, errorInfo), 392 LanguageList[l].Comment(comment+" [POINTER]")) 393 394 case *ssa.Field: 395 if register == "" { 396 comp.emitComment(comment) 397 } else { 398 st := instruction.(*ssa.Field).X.Type().Underlying().(*types.Struct) 399 fName := tgoutil.MakeID(st.Field(instruction.(*ssa.Field).Field).Name()) 400 l := comp.TargetLang 401 fmt.Fprintln(&LanguageList[l].buffer, 402 LanguageList[l].Field(register, instruction.(*ssa.Field).X, 403 instruction.(*ssa.Field).Field, fName, errorInfo, false)+ 404 LanguageList[l].Comment(comment)) 405 } 406 407 case *ssa.DebugRef: // TODO the comment could include the actual Go code 408 debugCode := "" 409 ident, ok := instruction.(*ssa.DebugRef).Expr.(*ast.Ident) 410 if ok { 411 if ident.Obj != nil { 412 if ident.Obj.Kind == ast.Var { 413 //fmt.Printf("DEBUGref %s (%s) => %s %+v %+v %+v\n", instruction.(*ssa.DebugRef).X.Name(), 414 // instruction.(*ssa.DebugRef).X.Type().String(), 415 // ident.Name, ident.Obj.Decl, ident.Obj.Data, ident.Obj.Type) 416 name := ident.Name 417 glob, isGlob := instruction.(*ssa.DebugRef).X.(*ssa.Global) 418 if isGlob { 419 name = glob.Pkg.String()[len("package "):] + "." + name 420 } 421 debugCode = LanguageList[l].DebugRef(name, instruction.(*ssa.DebugRef).X, errorInfo) 422 } 423 } 424 } 425 fmt.Fprintln(&LanguageList[l].buffer, debugCode+LanguageList[l].Comment(comment)) 426 427 case *ssa.Select: 428 text := LanguageList[l].Select(true, register, instruction, false, errorInfo) 429 fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment)) 430 431 default: 432 comp.emitComment(comment + " [NO CODE GENERATED]") 433 comp.LogError(errorInfo, "pogo", fmt.Errorf("SSA instruction not implemented: %v", reflect.TypeOf(instruction))) 434 } 435 if false { //TODO add instruction detail DEBUG FLAG 436 for o := range operands { // this loop for the creation of comments to show what is in the instructions 437 val := *operands[o] 438 vip := valIsPointer(val) 439 if vip { 440 vipOut := showIndirectValue(val) 441 comp.emitComment(fmt.Sprintf("Op[%d].VIP: %+v", o, vipOut)) 442 } else { 443 var ic interface{} = *operands[o] 444 constVal, isConst := ic.(*ssa.Const) 445 if isConst { 446 comp.emitComment(fmt.Sprintf("Op[%d]: Constant= %+v", o, constVal)) 447 } else { 448 comp.emitComment(fmt.Sprintf("Op[%d]: %v = %+v", o, (*operands[o]), val)) 449 } 450 } 451 // l := TargetLang 452 // fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Value(*operands[o], "TEST")) 453 } 454 } 455 return // return value is named and set in the code above 456 } 457 458 func getFnPath(fn *ssa.Function) string { 459 if fn == nil { 460 //println("DEBUG getFnPath nil function") 461 return "" 462 } 463 ob := fn.Object() 464 if ob == nil { 465 //println("DEBUG getFnPath nil object: name,synthetic=", fn.Name(), ",", fn.Synthetic) 466 return "" 467 } 468 pk := ob.Pkg() 469 if pk == nil { 470 //println("DEBUG getFnPath nil package: name,synthetic=", fn.Name(), ",", fn.Synthetic) 471 return "" 472 } 473 return pk.Path() 474 }