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