1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * binary_ops.go 12 * 13 * Created on Apr 12, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "go/ast" 21 "go/token" 22 r "reflect" 23 24 "github.com/cosmos72/gomacro/base/reflect" 25 xr "github.com/cosmos72/gomacro/xreflect" 26) 27 28:import ( 29 "fmt" 30 "go/ast" 31 "go/token" 32 r "reflect" 33) 34 35:func upcasefirstbyte(str string) string { 36 if len(str) > 0 && str[0] >= 'a' && str[0] <= 'z' { 37 bytes := []byte(str) 38 bytes[0] -= 'a' - 'A' 39 return string(bytes) 40 } 41 return str 42} 43 44:func makekind(typ ast.Node) ast.Node { 45 t := EvalType(typ) 46 47 // go/ast.SelectorExpr requires the foo in r.foo to be an *ast.Ident, cannot unquote there 48 kind := ~"{r . foo} 49 kind.Sel = &ast.Ident{Name: upcasefirstbyte(t.Name())} 50 return kind 51} 52 53:func convertvalue1(typ, val ast.Node) ast.Node { 54 var t r.Type = EvalType(typ) 55 if t == nil { 56 // keep the result wrapped in a reflect.Value 57 return val 58 } 59 // unwrap the result 60 tname := t.Name() 61 // remove final digits from t.Name() 62 // needed to convert Uint64 -> Uint etc. to calls reflect.Value.{tname} 63 for len(tname) != 0 { 64 ch := tname[len(tname)-1] 65 if ch < '0' || ch > '9' { 66 break 67 } 68 tname = tname[0:len(tname)-1] 69 } 70 if tname == "uintptr" { 71 tname = "uint" // use reflect.Value.Uint() 72 } 73 sel := ~"{~,val . foo} // we modify it destructively 74 sel.Sel = &ast.Ident{Name: upcasefirstbyte(tname)} 75 76 switch t.Kind() { 77 case r.Bool, r.Int64, r.Uint64, r.Float64, r.Complex128, r.String: 78 // result of reflect.Value.{tname} is already the correct type 79 val = ~"{~,sel ()} 80 default: 81 // convert int64, uint64... to the correct type 82 val = ~"{~,typ ( ~,sel () )} 83 } 84 return val 85} 86 87:macro binaryop(opnode, xconst, yconst, typ ast.Node) ast.Node { 88 89 // the return type of Eval() and EvalType() varies. better check early. 90 xc, yc := Eval(xconst).(bool), Eval(yconst).(bool) 91 optoken := Eval(opnode).(token.Token) 92 93 if xc == yc { 94 var expr *ast.BinaryExpr = ~"{x(env) && y(env)} // quasiquote, we modify it destructively 95 expr.Op = optoken 96 97 return ~"{ 98 x := x.(func(*Env) ~,typ) 99 y := y.(func(*Env) ~,typ) 100 fun = func(env *Env) ~,typ { 101 return ~,expr 102 } 103 } 104 } else if yc { 105 var expr *ast.BinaryExpr = ~"{x(env) && y} // quasiquote, we modify it destructively 106 expr.Op = optoken 107 108 converty := convertvalue1(typ, ~'{r.ValueOf(y)}) 109 return ~"{ 110 x := x.(func(*Env) ~,typ) 111 y := ~,converty 112 fun = func(env *Env) ~,typ { 113 return ~,expr 114 } 115 } 116 } else { 117 var expr *ast.BinaryExpr = ~"{x && y(env)} // quasiquote, we modify it destructively 118 expr.Op = optoken 119 120 convertx := convertvalue1(typ, ~'{r.ValueOf(x)}) 121 return ~"{ 122 x := ~,convertx 123 y := y.(func(*Env) ~,typ) 124 fun = func(env *Env) ~,typ { 125 return ~,expr 126 } 127 } 128 } 129} 130 131:macro binaryops(opnode, xconst, yconst, types ast.Node) ast.Node { 132 typelist := types.(*ast.BlockStmt).List 133 caselist := make([]ast.Stmt, 0, len(typelist)) 134 foundnil := false 135 for _, typ := range typelist { 136 t := EvalType(typ) 137 if t == nil { 138 caselist = append(caselist, ~"{default: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ}) 139 foundnil = true 140 141 } else if t.Kind() == r.Int { 142 // shortcut for all int* types 143 for _, typ := range []ast.Expr{~'int, ~'int8, ~'int16, ~'int32, ~'int64} { 144 kind := makekind(typ) 145 caselist = append(caselist, ~"{case ~,kind: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ}) 146 } 147 } else if t.Kind() == r.Uint { 148 // shortcut for all uint* types 149 for _, typ := range []ast.Expr{~'uint, ~'uint8, ~'uint16, ~'uint32, ~'uint64, ~'uintptr} { 150 kind := makekind(typ) 151 caselist = append(caselist, ~"{case ~,kind: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ}) 152 } 153 } else { 154 kind := makekind(typ) 155 caselist = append(caselist, ~"{case ~,kind: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ}) 156 } 157 } 158 159 if !foundnil { 160 caselist = append(caselist, ~'{default: return c.invalidBinaryExpr(node, xe, ye)}) 161 } 162 return ~"{ switch k { ~,@caselist } } 163} 164 165func (c *Comp) Add(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 166 xc, yc := xe.Const(), ye.Const() 167 c.toSameFuncType(node, xe, ye) 168 k := xe.Type.Kind() 169 170 // if both x and y are constants, BinaryExpr will invoke EvalConst() 171 // on our return value. no need to optimize that. 172 var fun I 173 if xc == yc { 174 x, y := xe.Fun, ye.Fun 175 {binaryops; token.ADD; false; false; { int; uint; float32; float64; complex64; complex128; string }} 176 } else if yc { 177 x := xe.Fun 178 y := ye.Value 179 if isLiteralNumber(y, 0) || y == "" { 180 return xe 181 } 182 {binaryops; token.ADD; false; true; { int; uint; float32; float64; complex64; complex128; string }} 183 } else { 184 x := xe.Value 185 y := ye.Fun 186 if isLiteralNumber(x, 0) || x == "" { 187 return ye 188 } 189 {binaryops; token.ADD; true; false; { int; uint; float32; float64; complex64; complex128; string }} 190 } 191 return exprFun(xe.Type, fun) 192} 193 194func (c *Comp) Sub(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 195 xc, yc := xe.Const(), ye.Const() 196 c.toSameFuncType(node, xe, ye) 197 k := xe.Type.Kind() 198 199 // if both x and y are constants, BinaryExpr will invoke EvalConst() 200 // on our return value. no need to optimize that. 201 var fun I 202 if xc == yc { 203 x, y := xe.Fun, ye.Fun 204 {binaryops; token.SUB; false; false; { int; uint; float32; float64; complex64; complex128 }} 205 } else if yc { 206 x := xe.Fun 207 y := ye.Value 208 if isLiteralNumber(y, 0) { 209 return xe 210 } 211 {binaryops; token.SUB; false; true; { int; uint; float32; float64; complex64; complex128 }} 212 } else { 213 x := xe.Value 214 y := ye.Fun 215 {binaryops; token.SUB; true; false; { int; uint; float32; float64; complex64; complex128 }} 216 } 217 return exprFun(xe.Type, fun) 218} 219 220func (c *Comp) Mul(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 221 xc, yc := xe.Const(), ye.Const() 222 c.toSameFuncType(node, xe, ye) 223 k := xe.Type.Kind() 224 225 // if both x and y are constants, BinaryExpr will invoke EvalConst() 226 // on our return value. no need to optimize that. 227 var fun I 228 if xc == yc { 229 x, y := xe.Fun, ye.Fun 230 {binaryops; token.MUL; false; false; { int; uint; float32; float64; complex64; complex128 }} 231 } else if yc { 232 x := xe.Fun 233 y := ye.Value 234 if ze := c.mulPow2(node, xe, ye); ze != nil { 235 return ze 236 } 237 {binaryops; token.MUL; false; true; { int; uint; float32; float64; complex64; complex128 }} 238 } else { 239 x := xe.Value 240 y := ye.Fun 241 if ze := c.mulPow2(node, xe, ye); ze != nil { 242 return ze 243 } 244 {binaryops; token.MUL; true; false; { int; uint; float32; float64; complex64; complex128 }} 245 } 246 return exprFun(xe.Type, fun) 247} 248 249func (c *Comp) Quo(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 250 xc, yc := xe.Const(), ye.Const() 251 c.toSameFuncType(node, xe, ye) 252 k := xe.Type.Kind() 253 254 // if both x and y are constants, BinaryExpr will invoke EvalConst() 255 // on our return value. no need to optimize that. 256 var fun I 257 if xc == yc { 258 x, y := xe.Fun, ye.Fun 259 {binaryops; token.QUO; false; false; { int; uint; float32; float64; complex64; complex128 }} 260 } else if yc { 261 x := xe.Fun 262 y := ye.Value 263 if isLiteralNumber(y, 0) { 264 c.Errorf("division by zero") 265 return nil 266 } else if ze := c.quoPow2(node, xe, ye); ze != nil { 267 return ze 268 } 269 {binaryops; token.QUO; false; true; { int; uint; float32; float64; complex64; complex128 }} 270 } else { 271 x := xe.Value 272 y := ye.Fun 273 {binaryops; token.QUO; true; false; { int; uint; float32; float64; complex64; complex128 }} 274 } 275 return exprFun(xe.Type, fun) 276} 277 278func (c *Comp) Rem(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 279 xc, yc := xe.Const(), ye.Const() 280 c.toSameFuncType(node, xe, ye) 281 k := xe.Type.Kind() 282 283 // if both x and y are constants, BinaryExpr will invoke EvalConst() 284 // on our return value. no need to optimize that. 285 var fun I 286 if xc == yc { 287 x, y := xe.Fun, ye.Fun 288 {binaryops; token.REM; false; false; { int; uint }} 289 } else if yc { 290 x := xe.Fun 291 y := ye.Value 292 // cannot optimize x % 1 to 0 because x may have side effects 293 if isLiteralNumber(y, 0) { 294 c.Errorf("division by zero") 295 return nil 296 } else if ze := c.remPow2(node, xe, ye); ze != nil { 297 return ze 298 } 299 {binaryops; token.REM; false; true; { int; uint }} 300 } else { 301 x := xe.Value 302 y := ye.Fun 303 {binaryops; token.REM; true; false; { int; uint }} 304 } 305 return exprFun(xe.Type, fun) 306} 307 308:macro mulpow2(typ ast.Node) ast.Node { 309 return ~"{ 310 x := x.(func(*Env) ~,typ) 311 if ypositive { 312 switch shift { 313 case 1: 314 fun = func(env *Env) ~,typ { 315 return x(env) << 1 316 } 317 case 2: 318 fun = func(env *Env) ~,typ { 319 return x(env) << 2 320 } 321 case 8: 322 fun = func(env *Env) ~,typ { 323 return x(env) << 8 324 } 325 default: 326 fun = func(env *Env) ~,typ { 327 return x(env) << shift 328 } 329 } 330 } else { 331 fun = func(env *Env) ~,typ { 332 return -(x(env) << shift) 333 } 334 } 335 } 336} 337 338:macro mulpow2_u(typ ast.Node) ast.Node { 339 return ~"{ 340 x := x.(func(*Env) ~,typ) 341 switch shift { 342 case 1: 343 fun = func(env *Env) ~,typ { 344 return x(env) << 1 345 } 346 case 2: 347 fun = func(env *Env) ~,typ { 348 return x(env) << 2 349 } 350 case 8: 351 fun = func(env *Env) ~,typ { 352 return x(env) << 8 353 } 354 default: 355 fun = func(env *Env) ~,typ { 356 return x(env) << shift 357 } 358 } 359 } 360} 361 362// mulPow2 tries to optimize multiplications by a constant power-of-two. 363// returns nil if no optimized version could be compiled. 364func (c *Comp) mulPow2(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 365 // no need to optimize if both xe and ye are constant: 366 // multiplication will be computed only once by EvalConst() 367 if xe.Const() == ye.Const() { 368 return nil 369 } 370 if xe.Const() { 371 // swap xe and ye. no side effects, xe is a constant 372 xe, ye = ye, xe 373 } 374 if isLiteralNumber(ye.Value, 0) { 375 return c.exprZero(xe) 376 } else if isLiteralNumber(ye.Value, 1) { 377 return xe 378 } else if isLiteralNumber(ye.Value, -1) { 379 node1 := &ast.UnaryExpr{OpPos: node.OpPos, Op: token.SUB, X: node.X} 380 return c.UnaryMinus(node1, xe) 381 } 382 ypositive := true 383 yv := r.ValueOf(ye.Value) 384 var y uint64 385 switch reflect.Category(yv.Kind()) { 386 case r.Int: 387 sy := yv.Int() 388 if sy < 0 { 389 ypositive = false 390 y = uint64(-sy) 391 } else { 392 y = uint64(sy) 393 } 394 case r.Uint: 395 y = yv.Uint() 396 default: 397 // floating point or complex multiplication 398 return nil 399 } 400 if !isPowerOfTwo(y) { 401 // multiplication by shift and add not implemented... 402 return nil 403 } 404 shift := integerLen(y) - 1 405 x := xe.Fun 406 var fun I 407 switch xe.Type.Kind() { 408 case r.Int: {mulpow2; int} 409 case r.Int8: {mulpow2; int8} 410 case r.Int16: {mulpow2; int16} 411 case r.Int32: {mulpow2; int32} 412 case r.Int64: {mulpow2; int64} 413 case r.Uint: {mulpow2_u; uint} 414 case r.Uint8: {mulpow2_u; uint8} 415 case r.Uint16: {mulpow2_u; uint16} 416 case r.Uint32: {mulpow2_u; uint32} 417 case r.Uint64: {mulpow2_u; uint64} 418 case r.Uintptr: {mulpow2_u; uintptr} 419 default: return nil 420 } 421 return exprFun(xe.Type, fun) 422} 423 424:macro quopow2(typ ast.Node) ast.Node { 425 return ~"{ 426 x := x.(func(*Env) ~,typ) 427 y_1 := ~,typ(y - 1) // cannot overflow, y was originally a ~,typ 428 if ypositive { 429 fun = func(env *Env) ~,typ { 430 n := x(env) 431 if n < 0 { 432 n += y_1 433 } 434 return n >> shift 435 } 436 } else { 437 fun = func(env *Env) ~,typ { 438 n := x(env) 439 if n < 0 { 440 n += y_1 441 } 442 return -(n >> shift) 443 } 444 } 445 } 446} 447 448:macro quopow2_u(typ ast.Node) ast.Node { 449 return ~"{ 450 x := x.(func(*Env) ~,typ) 451 fun = func(env *Env) ~,typ { 452 return x(env) >> shift 453 } 454 } 455} 456 457// quoPow2 tries to optimize divisions by a constant power-of-two. 458// returns nil if no optimized version could be compiled. 459func (c *Comp) quoPow2(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 460 // no need to optimize if both xe and ye are constant: 461 // division will be computed only once by EvalConst() 462 if xe.Const() || !ye.Const() { 463 return nil 464 } 465 if isLiteralNumber(ye.Value, 0) { 466 c.Errorf("division by zero") 467 return nil 468 } else if isLiteralNumber(ye.Value, 1) { 469 return xe 470 } else if isLiteralNumber(ye.Value, -1) { 471 node1 := &ast.UnaryExpr{OpPos: node.OpPos, Op: token.SUB, X: node.X} 472 return c.UnaryMinus(node1, xe) 473 } 474 ypositive := true 475 yv := r.ValueOf(ye.Value) 476 var y uint64 477 switch reflect.Category(yv.Kind()) { 478 case r.Int: 479 sy := yv.Int() 480 if sy < 0 { 481 ypositive = false 482 y = uint64(-sy) 483 } else { 484 y = uint64(sy) 485 } 486 case r.Uint: 487 y = yv.Uint() 488 default: 489 // floating point or complex division 490 return nil 491 } 492 if !isPowerOfTwo(y) { 493 // division by multiplication and shift not implemented... 494 return nil 495 } 496 // attention: xe / (2**n) and xe >> n have different truncation rules for negative xe: 497 // quotient / truncates toward zero 498 // right shift >> truncates toward negative infinity 499 // examples: 500 // 11 / 2 = 5, 11 >> 1 = 5 // same result 501 // -11 / 2 = -5, -11 >> 1 = -6 // different result 502 // 63 / 8 = 7, 63 >> 3 = 7 // same result 503 // -63 / 8 = -7, -63 >> 3 = -8 // different result 504 // 505 // -11 / -2 = 5, -(-11 >> 1) = 6 // different result 506 // -63 / -8 = 7, -(-63 >> 3) = 8 // different result 507 // 508 // to fix this, when xe is negative we must add abs(y)-1 to it: 509 // -11 / 2 = -5, (-11 + 1) >> 1 = -10 >> 1 = -5 // same result 510 // -63 / 8 = -7, (-63 + 7) >> 3 = -56 >> 3 = -7 // same result 511 // 512 // -11 / -2 = 5, -((-11 + 1) >> 1) = -(-10 >> 1) = 5 // same result 513 // -63 / -8 = 7, -((-63 + 7) >> 3) = -(-56 >> 3) = 7 // same result 514 515 shift := integerLen(y) - 1 516 x := xe.Fun 517 var fun I 518 switch xe.Type.Kind() { 519 case r.Int: {quopow2; int} 520 case r.Int8: {quopow2; int8} 521 case r.Int16: {quopow2; int16} 522 case r.Int32: {quopow2; int32} 523 case r.Int64: {quopow2; int64} 524 case r.Uint: {quopow2_u; uint} 525 case r.Uint8: {quopow2_u; uint8} 526 case r.Uint16: {quopow2_u; uint16} 527 case r.Uint32: {quopow2_u; uint32} 528 case r.Uint64: {quopow2_u; uint64} 529 case r.Uintptr: {quopow2_u; uintptr} 530 default: return nil 531 } 532 return exprFun(xe.Type, fun) 533} 534 535:macro rempow2(typ ast.Node) ast.Node { 536 return ~"{ 537 x := x.(func(*Env) ~,typ) 538 y_1 := ~,typ(y - 1) // cannot overflow, y was originally a ~,typ 539 fun = func(env *Env) ~,typ { 540 n := x(env) 541 if n >= 0 { 542 return n & y_1 543 } 544 return -(-n & y_1) 545 } 546 } 547} 548 549:macro rempow2_u(typ ast.Node) ast.Node { 550 return ~"{ 551 x := x.(func(*Env) ~,typ) 552 y_1 := ~,typ(y - 1) // cannot overflow, y was originally a ~,typ 553 fun = func(env *Env) ~,typ { 554 return x(env) & y_1 555 } 556 } 557} 558 559// remPow2 tries to optimize remainders by a constant power-of-two. 560// returns nil if no optimized version could be compiled. 561func (c *Comp) remPow2(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 562 // no need to optimize if both xe and ye are constant: 563 // remainder will be computed only once by EvalConst() 564 if xe.Const() || !ye.Const() { 565 return nil 566 } 567 if isLiteralNumber(ye.Value, 0) { 568 c.Errorf("division by zero") 569 return nil 570 } else if isLiteralNumber(ye.Value, 1) { 571 return c.exprZero(xe) 572 } 573 yv := r.ValueOf(ye.Value) 574 var y uint64 575 switch reflect.Category(yv.Kind()) { 576 case r.Int: 577 sy := yv.Int() 578 if sy < 0 { 579 y = uint64(-sy) 580 } else { 581 y = uint64(sy) 582 } 583 case r.Uint: 584 y = yv.Uint() 585 default: 586 // floating point or complex division 587 return nil 588 } 589 if !isPowerOfTwo(y) { 590 // remainder by multiplication and shift not implemented... 591 return nil 592 } 593 // attention: % (2**n) and & (2**n - 1) have different behaviours for negative xe: 594 // remainder % has the same sign as xe 595 // bitwise-and & is always >= 0 (for non-negative right operand) 596 // luckily, in Go x % y and x % -y always give the same result, so we can assume y >= 0 597 // examples: 598 // 11 % 2 = 1, 11 & 1 = 1 // same result 599 // -11 % 2 = -1, -11 & 1 = 1 // different result 600 // -11 % -2 = -1, -11 & 1 = 1 // different result 601 // 63 % 8 = 7, 63 & 7 = 7 // same result 602 // -63 % 8 = -7, -63 & 7 = 1 // different result 603 // -63 % -8 = -7, -63 & 7 = 1 // different result 604 // 605 // to fix this, when xe is negative, we flip its sign, perform the bitwise-and with (abs(y)-1), then flip again the sign: 606 // -11 % 2 = -1, -(11 & 1) = -1 // same result 607 // -11 % -2 = -1, -(11 & 1) = -1 // same result 608 // -63 % 8 = -7, -(63 & 7) = -7 // same result 609 // -63 % -8 = -7, -(63 & 7) = -7 // same result 610 611 x := xe.Fun 612 var fun I 613 switch xe.Type.Kind() { 614 case r.Int: {rempow2; int} 615 case r.Int8: {rempow2; int8} 616 case r.Int16: {rempow2; int16} 617 case r.Int32: {rempow2; int32} 618 case r.Int64: {rempow2; int64} 619 case r.Uint: {rempow2_u; uint} 620 case r.Uint8: {rempow2_u; uint8} 621 case r.Uint16: {rempow2_u; uint16} 622 case r.Uint32: {rempow2_u; uint32} 623 case r.Uint64: {rempow2_u; uint64} 624 case r.Uintptr: {rempow2_u; uintptr} 625 default: return nil 626 } 627 return exprFun(xe.Type, fun) 628} 629 630func isPowerOfTwo(n uint64) bool { 631 return n != 0 && (n&(n-1)) == 0 632} 633 634// integerLen returns the number of bits needed to represent n 635func integerLen(n uint64) uint8 { 636 var l uint8 637 for n > 0xff { 638 l += 8 639 n >>= 8 640 } 641 for n != 0 { 642 l++ 643 n >>= 1 644 } 645 return l 646} 647 648func (c *Comp) And(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 649 xc, yc := xe.Const(), ye.Const() 650 c.toSameFuncType(node, xe, ye) 651 k := xe.Type.Kind() 652 653 // if both x and y are constants, BinaryExpr will invoke EvalConst() 654 // on our return value. no need to optimize that. 655 var fun I 656 if xc == yc { 657 x, y := xe.Fun, ye.Fun 658 {binaryops; token.AND; false; false; { int; uint }} 659 } else if yc { 660 x := xe.Fun 661 y := ye.Value 662 if isLiteralNumber(y, 0) { 663 return c.exprZero(xe) 664 } else if isLiteralNumber(y, -1) { 665 return xe 666 } 667 {binaryops; token.AND; false; true; { int; uint }} 668 } else { 669 x := xe.Value 670 y := ye.Fun 671 if isLiteralNumber(x, 0) { 672 return c.exprZero(ye) 673 } else if isLiteralNumber(x, -1) { 674 return ye 675 } 676 {binaryops; token.AND; true; false; { int; uint }} 677 } 678 return exprFun(xe.Type, fun) 679} 680 681func (c *Comp) Or(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 682 xc, yc := xe.Const(), ye.Const() 683 c.toSameFuncType(node, xe, ye) 684 k := xe.Type.Kind() 685 686 // if both x and y are constants, BinaryExpr will invoke EvalConst() 687 // on our return value. no need to optimize that. 688 var fun I 689 if xc == yc { 690 x, y := xe.Fun, ye.Fun 691 {binaryops; token.OR; false; false; { int; uint }} 692 } else if yc { 693 x := xe.Fun 694 y := ye.Value 695 // cannot optimize x | -1 to -1 because x may have side effects 696 if isLiteralNumber(y, 0) { 697 return xe 698 } 699 {binaryops; token.OR; false; true; { int; uint }} 700 } else { 701 x := xe.Value 702 y := ye.Fun 703 // cannot optimize -1 & y to -1 because x may have side effects 704 if isLiteralNumber(x, 0) { 705 return ye 706 } 707 {binaryops; token.OR; true; false; { int; uint }} 708 } 709 return exprFun(xe.Type, fun) 710} 711 712func (c *Comp) Xor(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 713 xc, yc := xe.Const(), ye.Const() 714 c.toSameFuncType(node, xe, ye) 715 k := xe.Type.Kind() 716 717 // if both x and y are constants, BinaryExpr will invoke EvalConst() 718 // on our return value. no need to optimize that. 719 var fun I 720 if xc == yc { 721 x, y := xe.Fun, ye.Fun 722 {binaryops; token.XOR; false; false; { int; uint }} 723 } else if yc { 724 x := xe.Fun 725 y := ye.Value 726 if isLiteralNumber(y, 0) { 727 return xe 728 } 729 {binaryops; token.XOR; false; true; { int; uint }} 730 } else { 731 x := xe.Value 732 y := ye.Fun 733 if isLiteralNumber(x, 0) { 734 return ye 735 } 736 {binaryops; token.XOR; true; false; { int; uint }} 737 } 738 return exprFun(xe.Type, fun) 739} 740 741func (c *Comp) Andnot(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr { 742 xc, yc := xe.Const(), ye.Const() 743 c.toSameFuncType(node, xe, ye) 744 k := xe.Type.Kind() 745 746 // if both x and y are constants, BinaryExpr will invoke EvalConst() 747 // on our return value. no need to optimize that. 748 var fun I 749 if xc == yc { 750 x, y := xe.Fun, ye.Fun 751 {binaryops; token.AND_NOT; false; false; { int; uint }} 752 } else if yc { 753 x := xe.Fun 754 y := ye.Value 755 if isLiteralNumber(y, -1) { 756 return c.exprZero(xe) 757 } else if isLiteralNumber(y, 0) { 758 return xe 759 } 760 {binaryops; token.AND_NOT; false; true; { int; uint }} 761 } else { 762 x := xe.Value 763 y := ye.Fun 764 if isLiteralNumber(x, 0) { 765 return c.exprZero(ye) 766 } 767 {binaryops; token.AND_NOT; true; false; { int; uint }} 768 } 769 return exprFun(xe.Type, fun) 770} 771 772:macro exprzero(typ ast.Node) ast.Node { 773 if EvalType(typ) == nil { 774 return ~"{ 775 zero := xr.Zero(t) 776 x := funAsX1(x, nil) 777 fun = func(env *Env) r.Value { 778 x(env) 779 return zero 780 } 781 } 782 } 783 return ~"{ 784 x := x.(func(*Env) ~,typ) 785 fun = func(env *Env) (zero ~,typ) { 786 x(env) 787 return 788 } 789 } 790} 791 792:macro exprzeros(types ast.Node) ast.Node { 793 typelist := types.(*ast.BlockStmt).List 794 caselist := make([]ast.Stmt, 0, len(typelist)) 795 foundnil := false 796 for _, typ := range typelist { 797 t := EvalType(typ) 798 if t == nil { 799 continue 800 } else if t.Kind() == r.Int { 801 // shortcut for all int* types 802 for _, typ := range []ast.Expr{~'int, ~'int8, ~'int16, ~'int32, ~'int64} { 803 kind := makekind(typ) 804 caselist = append(caselist, ~"{case ~,kind: exprzero; ~,typ}) 805 } 806 } else if t.Kind() == r.Uint { 807 // shortcut for all uint* types 808 for _, typ := range []ast.Expr{~'uint, ~'uint8, ~'uint16, ~'uint32, ~'uint64, ~'uintptr} { 809 kind := makekind(typ) 810 caselist = append(caselist, ~"{case ~,kind: exprzero; ~,typ}) 811 } 812 } else { 813 kind := makekind(typ) 814 caselist = append(caselist, ~"{case ~,kind: exprzero; ~,typ}) 815 } 816 } 817 818 caselist = append(caselist, ~"{default: exprzero; nil}) 819 820 return ~"{ switch k { ~,@caselist } } 821} 822 823// exprZero compiles a function that evaluates xe, 824// then discards the result and always returns zero 825func (c *Comp) exprZero(xe *Expr) *Expr { 826 if xe.Const() { 827 xe.ConstTo(xe.DefaultType()) 828 return c.exprValue(xe.Type, xr.Zero(xe.Type).Interface()) 829 } 830 t := xe.Type 831 k := t.Kind() 832 x := xe.Fun 833 var fun I 834 {exprzeros; {bool; int; uint; float32; float64; complex64; complex128; string} } 835 return exprFun(t, fun) 836} 837