1// Copyright 2015 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 ssa 6 7import ( 8 "cmd/compile/internal/types" 9 "cmd/internal/src" 10 "testing" 11) 12 13func TestDeadStore(t *testing.T) { 14 c := testConfig(t) 15 ptrType := c.config.Types.BytePtr 16 t.Logf("PTRTYPE %v", ptrType) 17 fun := c.Fun("entry", 18 Bloc("entry", 19 Valu("start", OpInitMem, types.TypeMem, 0, nil), 20 Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil), 21 Valu("v", OpConstBool, c.config.Types.Bool, 1, nil), 22 Valu("addr1", OpAddr, ptrType, 0, nil, "sb"), 23 Valu("addr2", OpAddr, ptrType, 0, nil, "sb"), 24 Valu("addr3", OpAddr, ptrType, 0, nil, "sb"), 25 Valu("zero1", OpZero, types.TypeMem, 1, c.config.Types.Bool, "addr3", "start"), 26 Valu("store1", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "zero1"), 27 Valu("store2", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr2", "v", "store1"), 28 Valu("store3", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "store2"), 29 Valu("store4", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr3", "v", "store3"), 30 Goto("exit")), 31 Bloc("exit", 32 Exit("store3"))) 33 34 CheckFunc(fun.f) 35 dse(fun.f) 36 CheckFunc(fun.f) 37 38 v1 := fun.values["store1"] 39 if v1.Op != OpCopy { 40 t.Errorf("dead store not removed") 41 } 42 43 v2 := fun.values["zero1"] 44 if v2.Op != OpCopy { 45 t.Errorf("dead store (zero) not removed") 46 } 47} 48 49func TestDeadStorePhi(t *testing.T) { 50 // make sure we don't get into an infinite loop with phi values. 51 c := testConfig(t) 52 ptrType := c.config.Types.BytePtr 53 fun := c.Fun("entry", 54 Bloc("entry", 55 Valu("start", OpInitMem, types.TypeMem, 0, nil), 56 Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil), 57 Valu("v", OpConstBool, c.config.Types.Bool, 1, nil), 58 Valu("addr", OpAddr, ptrType, 0, nil, "sb"), 59 Goto("loop")), 60 Bloc("loop", 61 Valu("phi", OpPhi, types.TypeMem, 0, nil, "start", "store"), 62 Valu("store", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr", "v", "phi"), 63 If("v", "loop", "exit")), 64 Bloc("exit", 65 Exit("store"))) 66 67 CheckFunc(fun.f) 68 dse(fun.f) 69 CheckFunc(fun.f) 70} 71 72func TestDeadStoreTypes(t *testing.T) { 73 // Make sure a narrow store can't shadow a wider one. We test an even 74 // stronger restriction, that one store can't shadow another unless the 75 // types of the address fields are identical (where identicalness is 76 // decided by the CSE pass). 77 c := testConfig(t) 78 t1 := c.config.Types.UInt64.PtrTo() 79 t2 := c.config.Types.UInt32.PtrTo() 80 fun := c.Fun("entry", 81 Bloc("entry", 82 Valu("start", OpInitMem, types.TypeMem, 0, nil), 83 Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil), 84 Valu("v", OpConstBool, c.config.Types.Bool, 1, nil), 85 Valu("addr1", OpAddr, t1, 0, nil, "sb"), 86 Valu("addr2", OpAddr, t2, 0, nil, "sb"), 87 Valu("store1", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "start"), 88 Valu("store2", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr2", "v", "store1"), 89 Goto("exit")), 90 Bloc("exit", 91 Exit("store2"))) 92 93 CheckFunc(fun.f) 94 cse(fun.f) 95 dse(fun.f) 96 CheckFunc(fun.f) 97 98 v := fun.values["store1"] 99 if v.Op == OpCopy { 100 t.Errorf("store %s incorrectly removed", v) 101 } 102} 103 104func TestDeadStoreUnsafe(t *testing.T) { 105 // Make sure a narrow store can't shadow a wider one. The test above 106 // covers the case of two different types, but unsafe pointer casting 107 // can get to a point where the size is changed but type unchanged. 108 c := testConfig(t) 109 ptrType := c.config.Types.UInt64.PtrTo() 110 fun := c.Fun("entry", 111 Bloc("entry", 112 Valu("start", OpInitMem, types.TypeMem, 0, nil), 113 Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil), 114 Valu("v", OpConstBool, c.config.Types.Bool, 1, nil), 115 Valu("addr1", OpAddr, ptrType, 0, nil, "sb"), 116 Valu("store1", OpStore, types.TypeMem, 0, c.config.Types.Int64, "addr1", "v", "start"), // store 8 bytes 117 Valu("store2", OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "store1"), // store 1 byte 118 Goto("exit")), 119 Bloc("exit", 120 Exit("store2"))) 121 122 CheckFunc(fun.f) 123 cse(fun.f) 124 dse(fun.f) 125 CheckFunc(fun.f) 126 127 v := fun.values["store1"] 128 if v.Op == OpCopy { 129 t.Errorf("store %s incorrectly removed", v) 130 } 131} 132 133func TestDeadStoreSmallStructInit(t *testing.T) { 134 c := testConfig(t) 135 ptrType := c.config.Types.BytePtr 136 typ := types.NewStruct([]*types.Field{ 137 types.NewField(src.NoXPos, &types.Sym{Name: "A"}, c.config.Types.Int), 138 types.NewField(src.NoXPos, &types.Sym{Name: "B"}, c.config.Types.Int), 139 }) 140 name := c.Temp(typ) 141 fun := c.Fun("entry", 142 Bloc("entry", 143 Valu("start", OpInitMem, types.TypeMem, 0, nil), 144 Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil), 145 Valu("zero", OpConst64, c.config.Types.Int, 0, nil), 146 Valu("v6", OpLocalAddr, ptrType, 0, name, "sp", "start"), 147 Valu("v3", OpOffPtr, ptrType, 8, nil, "v6"), 148 Valu("v22", OpOffPtr, ptrType, 0, nil, "v6"), 149 Valu("zerostore1", OpStore, types.TypeMem, 0, c.config.Types.Int, "v22", "zero", "start"), 150 Valu("zerostore2", OpStore, types.TypeMem, 0, c.config.Types.Int, "v3", "zero", "zerostore1"), 151 Valu("v8", OpLocalAddr, ptrType, 0, name, "sp", "zerostore2"), 152 Valu("v23", OpOffPtr, ptrType, 8, nil, "v8"), 153 Valu("v25", OpOffPtr, ptrType, 0, nil, "v8"), 154 Valu("zerostore3", OpStore, types.TypeMem, 0, c.config.Types.Int, "v25", "zero", "zerostore2"), 155 Valu("zerostore4", OpStore, types.TypeMem, 0, c.config.Types.Int, "v23", "zero", "zerostore3"), 156 Goto("exit")), 157 Bloc("exit", 158 Exit("zerostore4"))) 159 160 fun.f.Name = "smallstructinit" 161 CheckFunc(fun.f) 162 cse(fun.f) 163 dse(fun.f) 164 CheckFunc(fun.f) 165 166 v1 := fun.values["zerostore1"] 167 if v1.Op != OpCopy { 168 t.Errorf("dead store not removed") 169 } 170 v2 := fun.values["zerostore2"] 171 if v2.Op != OpCopy { 172 t.Errorf("dead store not removed") 173 } 174} 175