github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/haxe/ops.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 haxe 6 7 import ( 8 "fmt" 9 "go/types" 10 11 "golang.org/x/tools/go/ssa" 12 ) 13 14 func (l langType) codeUnOp(regTyp types.Type, op string, v interface{}, CommaOK bool, errorInfo string) string { 15 useInt64 := false 16 lt := l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) 17 if lt == "GOint64" { 18 useInt64 = true 19 } 20 rt := l.LangType(regTyp.Underlying(), false, errorInfo) 21 if lt != rt && op != "<-" && op != "*" { 22 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): result type %s != source type %s", rt, lt)) 23 } 24 25 // neko target platform requires special handling because in makes whole-number Float into Int without asking 26 // see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but not fixed as at 2013.9.6 27 28 switch op { 29 case "<-": 30 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): impossible to reach <- code")) 31 return "" 32 case "*": 33 goTyp := v.(ssa.Value).Type().Underlying().(*types.Pointer).Elem().Underlying() 34 35 //lt = l.LangType(goTyp, false, errorInfo) 36 iVal := "" + l.IndirectValue(v, errorInfo) + "" // need to cast it to pointer, when using -dce full and closures 37 //switch lt { 38 //case "Int": 39 // return "(" + iVal + ".load()|0)" + fmt.Sprintf("/* %v %s */", goTyp, loadStoreSuffix(goTyp)) // force to Int for js, compiled platforms should optimize this away 40 //default: 41 //if strings.HasPrefix(lt, "Pointer") { 42 // return "({var _v:PointerIF=" + iVal + `.load(); _v;})` // Ensure Haxe can work out that it is a pointer being returned 43 //} 44 if l.is1usePtr(v) { 45 oup, found := l.hc.map1usePtr[v.(ssa.Value)] 46 if !found { 47 panic(fmt.Sprintf("haxe.codeUnOp can't find oneUsePtr: %#v %s val %s=%s", 48 l.hc.map1usePtr, errorInfo, v.(ssa.Value).Name(), v.(ssa.Value).String())) 49 } 50 return oup.obj + ".get" + loadStoreSuffix(goTyp, true) + oup.off + ")" 51 } 52 if l.PogoComp().DebugFlag { 53 iVal = "Pointer.check(" + iVal + ")" 54 } 55 return iVal + ".load" + loadStoreSuffix(goTyp, false) + ")" + fmt.Sprintf("/* %v */ ", goTyp) 56 //} 57 case "-": 58 if l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) == "Complex" { 59 return "Complex.neg(" + l.IndirectValue(v, errorInfo) + ")" 60 } 61 fallthrough 62 default: 63 if useInt64 { 64 switch op { // roughly in the order of the GOint64 api spec 65 case "-": 66 return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(), 67 "GOint64.neg("+l.IndirectValue(v, errorInfo)+")", errorInfo) 68 case "^": 69 return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(), 70 "GOint64.xor("+l.IndirectValue(v, errorInfo)+",GOint64.make(-1,-1))", errorInfo) 71 default: 72 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): unhandled Int64 un-op: %s", op)) 73 return "" 74 } 75 } else { 76 valStr := l.IndirectValue(v, errorInfo) 77 switch v.(ssa.Value).Type().Underlying().(*types.Basic).Kind() { 78 case types.Uintptr: // although held as Dynamic, uintpointers are integers when doing calculations 79 valStr = "Force.toUint32(Force.toInt(" + valStr + "))" 80 case types.Float32: 81 valStr = "Force.toFloat32(" + valStr + ")" 82 case types.Float64: 83 valStr = "Force.toFloat(" + valStr + ")" 84 } 85 switch op { 86 case "^": 87 // Haxe has a different operator for bit-wise complement 88 return l.intTypeCoersion(regTyp.Underlying(), 89 "(~"+valStr+")", errorInfo) 90 case "-": //both negation and bit-complement can overflow 91 return l.intTypeCoersion(regTyp.Underlying(), 92 "(-"+valStr+")", errorInfo) 93 default: //no overflow issues, but let's be on the safe-side... 94 return l.intTypeCoersion(regTyp.Underlying(), 95 "("+op+valStr+")", errorInfo) 96 } 97 } 98 } 99 } 100 101 func (l langType) UnOp(register string, regTyp types.Type, op string, v interface{}, CommaOK bool, errorInfo string) string { 102 if op == "<-" { // wait for a channel to be ready 103 return l.Select(false, register, v, CommaOK, errorInfo) 104 } 105 return register + "=" + l.codeUnOp(regTyp, op, v, CommaOK, errorInfo) + ";" 106 } 107 108 func (l langType) codeBinOp(regTyp types.Type, op string, v1, v2 interface{}, errorInfo string) string { 109 ret := "" 110 useInt64 := false 111 v1LangType := l.LangType(v1.(ssa.Value).Type().Underlying(), false, errorInfo) 112 v2LangType := l.LangType(v2.(ssa.Value).Type().Underlying(), false, errorInfo) 113 if v1LangType != v2LangType && !(v1LangType == "Int" && v2LangType == "GOint64") && !(op == "<<" || op == ">>") { 114 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): haxe types not equal: %s %s %s", 115 v1LangType, op, v2LangType)) 116 return "" 117 } 118 rt := l.LangType(regTyp.Underlying(), false, errorInfo) 119 if v1LangType != rt && rt != "Bool" { 120 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): result type %s != 1st operand type %s", 121 rt, v1LangType)) 122 } 123 124 v1string := l.IndirectValue(v1, errorInfo) 125 v2string := l.IndirectValue(v2, errorInfo) 126 if v1LangType == "GOint64" { 127 useInt64 = true 128 } 129 130 // neko target platform requires special handling because in makes whole-number Float into Int without asking 131 // see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but not fixed as at 2013.9.6 132 switch v1LangType { 133 case "Float": 134 v1string = "Force.toFloat(" + v1string + ")" 135 case "Dynamic": // assume it is a uintptr, so integer arithmetic is required 136 v1string = "Force.toUint32(Force.toInt(" + v1string + "))" 137 } 138 switch v2LangType { 139 case "Float": 140 v2string = "Force.toFloat(" + v2string + ")" 141 case "Dynamic": // assume it is a uintptr, so integer arithmetic is required 142 v2string = "Force.toUint32(Force.toInt(" + v2string + "))" 143 } 144 145 if v1LangType == "Complex" { 146 switch op { 147 case "+": 148 return "Complex.add(" + v1string + "," + v2string + ")" 149 case "/": // TODO review divide by zero error handling for this case (currently in Haxe Complex class) 150 return "Complex.div(" + v1string + "," + v2string + ")" 151 case "*": 152 return "Complex.mul(" + v1string + "," + v2string + ")" 153 case "-": 154 return "Complex.sub(" + v1string + "," + v2string + ")" 155 case "==": 156 return "Complex.eq(" + v1string + "," + v2string + ")" 157 case "!=": 158 return "Complex.neq(" + v1string + "," + v2string + ")" 159 default: 160 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Complex op: %s", op)) 161 return "" 162 } 163 164 } else if v1LangType == "String" { 165 //switch op { 166 //case ">", "<", "<=", ">=": 167 // return "(Go_haxegoruntime_StringCompare.callFromRT(this._goroutine," + v1string + "," + v2string + 168 // ")" + op + "0)" 169 //default: 170 return "(" + v1string + op + v2string + ")" 171 //} 172 173 } else if v1LangType == "Interface" { 174 switch op { 175 case "==": 176 return "Interface.isEqual(" + v1string + "," + v2string + ")" 177 case "!=": 178 return "!Interface.isEqual(" + v1string + "," + v2string + ")" 179 default: 180 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Interface op: %s", op)) 181 return "" 182 } 183 184 } else if v1LangType == "Pointer" { 185 switch op { 186 case "==": 187 return "Pointer.isEqual(" + v1string + "," + v2string + ")" 188 case "!=": 189 return "!Pointer.isEqual(" + v1string + "," + v2string + ")" 190 default: 191 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Pointer op: %s", op)) 192 return "" 193 } 194 195 } else if v1LangType == "Object" { 196 switch op { 197 case "==": 198 return "(" + v1string + ".isEqual(0," + v2string + ",0))" 199 case "!=": 200 return "!(" + v1string + ".isEqual(0," + v2string + ",0))" 201 default: 202 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Object op: %s", op)) 203 return "" 204 } 205 206 } else { 207 if useInt64 { // explicitly enumerate all of the Int64 functions 208 isSignedStr := "true" 209 if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 { 210 isSignedStr = "false" 211 } 212 213 if op == "<<" || op == ">>" { 214 v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 215 } 216 217 switch op { // roughly in the order of the GOint64 api spec 218 case "+": 219 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 220 "GOint64.add("+v1string+","+v2string+")", errorInfo) 221 case "&": 222 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 223 "GOint64.and("+v1string+","+v2string+")", errorInfo) 224 case "/": 225 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 226 "GOint64.div("+v1string+","+v2string+","+isSignedStr+")", errorInfo) 227 case "%": 228 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 229 "GOint64.mod("+v1string+","+v2string+","+isSignedStr+")", errorInfo) 230 case "*": 231 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 232 "GOint64.mul("+v1string+","+v2string+")", errorInfo) 233 case "|": 234 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 235 "GOint64.or("+v1string+","+v2string+")", errorInfo) 236 case "<<": 237 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 238 "GOint64.shl("+v1string+","+v2string+")", errorInfo) 239 case ">>": 240 if isSignedStr == "true" { 241 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 242 "GOint64.shr("+v1string+","+v2string+")", errorInfo) // GOint64.shr does sign extension 243 } else { 244 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 245 "GOint64.ushr("+v1string+","+v2string+")", errorInfo) // GOint64.ushr does not do sign extension 246 } 247 case "-": 248 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 249 "GOint64.sub("+v1string+","+v2string+")", errorInfo) 250 case "^": 251 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 252 "GOint64.xor("+v1string+","+v2string+")", errorInfo) 253 case "&^": 254 v2string = "GOint64.xor(" + v2string + ",GOint64.make(-1,-1))" 255 ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(), 256 "GOint64.and("+v1string+","+v2string+")", errorInfo) 257 case "==", "!=", "<", ">", "<=", ">=": 258 compFunc := "GOint64.compare(" 259 if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 { 260 compFunc = "GOint64.ucompare(" 261 } 262 ret = "(" + compFunc + v1string + "," + v2string + ")" + op + "0)" 263 default: 264 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled 64-bit op: %s", op)) 265 return "" 266 } 267 268 } else { 269 switch op { // standard case, use Haxe operators 270 case "==", "!=", "<", ">", "<=", ">=": // no integer coersion, boolian results 271 switch v1.(ssa.Value).Type().Underlying().(type) { 272 case *types.Basic: 273 if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 { 274 if v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() == types.Uintptr { 275 // could be comparing pointers cast to uintptr, so force to uint 276 v1string = wrapForceToUInt(v1string, v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 277 } 278 if v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind() == types.Uintptr { 279 // could be comparing pointers cast to uintptr, so force to uint 280 v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 281 } 282 ret = "(Force.uintCompare(" + v1string + "," + v2string + ")" + op + "0)" 283 } else { 284 switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() { 285 case types.Float32: 286 // make sure we only compare the float32 bits 287 ret = "(" + 288 "Force.toFloat32(" + 289 v1string + ")" + op + 290 "Force.toFloat32(" + 291 v2string + ")" + ")" 292 default: 293 ret = "(" + v1string + op + v2string + ")" 294 } 295 } 296 default: 297 ret = "(" + v1string + op + v2string + ")" 298 } 299 case ">>", "<<": 300 //v1string = wrapForceToUInt(v1string, v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 301 v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 302 switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() { 303 case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned bit shift 304 if op == ">>" { 305 op = ">>>" // logical right shift if unsigned 306 } 307 } 308 bitlenMinus1 := fmt.Sprintf("%d", (haxeStdSizes.Sizeof(v1.(ssa.Value).Type().Underlying())*8)-1) 309 // TODO consider putting this code in a Haxe function 310 ret = "({var _v1:Int=" + v1string + " ; var _v2:Int=" + v2string + " ; _v2==0?_v1" //NoOp if v2==0 311 // js requires this out-of-range test - TODO check other targets 312 ret += ":(_v2<0||_v2>" + bitlenMinus1 + "?" // outside chance very large shift appears -ve 313 switch op { 314 case ">>": // signed right shift >= bitlen 315 ret += "(_v1&(1<<" + bitlenMinus1 + ")!=0?-1:0)" // the sign must be extended if -ve 316 case ">>>": // unsigned right shift >= bitlen 317 ret += "0" 318 case "<<": // left shift >= bitlen 319 ret += "0" 320 default: 321 panic("haxe unhandled shift operation") 322 } 323 ret += ":_v1" + op + "_v2);})" // the actual op 324 325 case "/": 326 switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() { 327 case types.Int8: 328 ret = "Force.intDiv(" + v1string + "," + v2string + ",1)" // 1 byte special processing 329 case types.Int16: 330 ret = "Force.intDiv(" + v1string + "," + v2string + ",2)" // 2 byte special processing 331 case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32 332 ret = "Force.intDiv(" + v1string + "," + v2string + ",4)" // 4 byte special processing 333 case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned division 334 ret = "Force.intDiv(" + v1string + "," + v2string + ",0)" // spec does not require special processing, but is unsigned 335 case types.UntypedFloat, types.Float32, types.Float64: 336 ret = "Force.floatDiv(" + v1string + "," + v2string + ")" 337 default: 338 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type")) 339 ret = "(ERROR)" 340 } 341 case "%": 342 switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() { 343 case types.Int8: 344 ret = "Force.intMod(" + v1string + "," + v2string + ", 1)" 345 case types.Int16: 346 ret = "Force.intMod(" + v1string + "," + v2string + ", 2)" 347 case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32 348 ret = "Force.intMod(" + v1string + "," + v2string + ", 4)" 349 case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned mod 350 ret = "Force.intMod(" + v1string + "," + v2string + ", 0)" 351 case types.UntypedFloat, types.Float32, types.Float64: 352 ret = "Force.floatMod(" + v1string + "," + v2string + ")" 353 default: 354 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type")) 355 ret = "(ERROR)" 356 } 357 358 case "*": 359 switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() { 360 case types.Int8: 361 ret = "Force.intMul(" + v1string + "," + v2string + ", 1)" 362 case types.Int16: 363 ret = "Force.intMul(" + v1string + "," + v2string + ", 2)" 364 case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32 365 ret = "Force.intMul(" + v1string + "," + v2string + ", 4)" 366 case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned mod 367 ret = "Force.intMul(" + v1string + "," + v2string + ", 0)" 368 case types.UntypedFloat, types.Float32, types.Float64: 369 ret = "(" + v1string + "*" + v2string + ")" 370 default: 371 l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type")) 372 ret = "(ERROR)" 373 } 374 375 case "&^": 376 // Haxe has a different operator for bit-wise complement ~, but using xor below 377 ret = "((" + v1string + ")&((" + v2string + ")^0xffffffff))" 378 379 case "&", "|", "^": 380 //v1string = wrapForceToUInt(v1string, v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 381 //v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind()) 382 ret = "((" + v1string + ")" + op + "(" + v2string + "))" 383 384 case "+", "-": 385 ret = "(" + v1string + op + v2string + ")" 386 387 default: 388 panic("haxe unhandled binary operator: " + op) 389 } 390 ret = l.intTypeCoersion( 391 regTyp.Underlying(), 392 ret, errorInfo) 393 394 } 395 return ret 396 } 397 } 398 399 func (l langType) BinOp(register string, regTyp types.Type, op string, v1, v2 interface{}, errorInfo string) string { 400 return register + "=" + l.codeBinOp(regTyp, op, v1, v2, errorInfo) + ";" 401 }