blob: dcacae7480c2975c50182aeb72d46d31e374d4f4 [file] [log] [blame]
Dan Willemsencc753b72021-08-31 13:25:42 -07001// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package noder
6
7import (
8 "go/constant"
9
10 "cmd/compile/internal/base"
11 "cmd/compile/internal/syntax"
12 "cmd/compile/internal/types"
13 "cmd/compile/internal/types2"
14)
15
16// match reports whether types t1 and t2 are consistent
17// representations for a given expression's type.
18func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
19 tuple, ok := t2.(*types2.Tuple)
20 if !ok {
21 // Not a tuple; can use simple type identity comparison.
22 return types.Identical(t1, g.typ(t2))
23 }
24
25 if hasOK {
26 // For has-ok values, types2 represents the expression's type as a
27 // 2-element tuple, whereas ir just uses the first type and infers
28 // that the second type is boolean. Must match either, since we
29 // sometimes delay the transformation to the ir form.
30 if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) {
31 return true
32 }
33 return types.Identical(t1, g.typ(t2))
34 }
35
36 if t1 == nil || tuple == nil {
37 return t1 == nil && tuple == nil
38 }
39 if !t1.IsFuncArgStruct() {
40 return false
41 }
42 if t1.NumFields() != tuple.Len() {
43 return false
44 }
45 for i, result := range t1.FieldSlice() {
46 if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) {
47 return false
48 }
49 }
50 return true
51}
52
53func (g *irgen) validate(n syntax.Node) {
54 switch n := n.(type) {
55 case *syntax.CallExpr:
56 tv := g.info.Types[n.Fun]
57 if tv.IsBuiltin() {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -080058 fun := n.Fun
59 for {
60 builtin, ok := fun.(*syntax.ParenExpr)
61 if !ok {
62 break
63 }
64 fun = builtin.X
65 }
66 switch builtin := fun.(type) {
Dan Willemsencc753b72021-08-31 13:25:42 -070067 case *syntax.Name:
68 g.validateBuiltin(builtin.Value, n)
69 case *syntax.SelectorExpr:
70 g.validateBuiltin(builtin.Sel.Value, n)
71 default:
72 g.unhandled("builtin", n)
73 }
74 }
75 }
76}
77
78func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
79 switch name {
80 case "Alignof", "Offsetof", "Sizeof":
81 // Check that types2+gcSizes calculates sizes the same
82 // as cmd/compile does.
83
Dan Willemsenbc60c3c2021-12-15 01:09:00 -080084 tv := g.info.Types[call]
85 if !tv.IsValue() {
86 base.FatalfAt(g.pos(call), "expected a value")
87 }
88
89 if tv.Value == nil {
90 break // unsafe op is not a constant, so no further validation
91 }
92
93 got, ok := constant.Int64Val(tv.Value)
Dan Willemsencc753b72021-08-31 13:25:42 -070094 if !ok {
95 base.FatalfAt(g.pos(call), "expected int64 constant value")
96 }
97
98 want := g.unsafeExpr(name, call.ArgList[0])
99 if got != want {
100 base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want)
101 }
102 }
103}
104
105// unsafeExpr evaluates the given unsafe builtin function on arg.
106func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 {
107 switch name {
108 case "Alignof":
109 return g.typ(g.info.Types[arg].Type).Alignment()
110 case "Sizeof":
111 return g.typ(g.info.Types[arg].Type).Size()
112 }
113
114 // Offsetof
115
116 sel := arg.(*syntax.SelectorExpr)
117 selection := g.info.Selections[sel]
118
119 typ := g.typ(g.info.Types[sel.X].Type)
120 typ = deref(typ)
121
122 var offset int64
123 for _, i := range selection.Index() {
124 // Ensure field offsets have been calculated.
125 types.CalcSize(typ)
126
127 f := typ.Field(i)
128 offset += f.Offset
129 typ = f.Type
130 }
131 return offset
132}