Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 //===- subzero/src/IceASanInstrumentation.cpp - ASan ------------*- C++ -*-===// | 1 //===- subzero/src/IceASanInstrumentation.cpp - ASan ------------*- C++ -*-===// |
| 2 // | 2 // |
| 3 // The Subzero Code Generator | 3 // The Subzero Code Generator |
| 4 // | 4 // |
| 5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
| 7 // | 7 // |
| 8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
| 9 /// | 9 /// |
| 10 /// \file | 10 /// \file |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 | 24 |
| 25 #include <sstream> | 25 #include <sstream> |
| 26 #include <unordered_map> | 26 #include <unordered_map> |
| 27 #include <unordered_set> | 27 #include <unordered_set> |
| 28 #include <vector> | 28 #include <vector> |
| 29 | 29 |
| 30 namespace Ice { | 30 namespace Ice { |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 constexpr SizeT RzSize = 32; | |
| 35 constexpr SizeT ShadowScaleLog2 = 3; | |
| 36 constexpr SizeT ShadowScale = 1 << ShadowScaleLog2; | |
| 37 constexpr SizeT ShadowLength32 = 1 << (32 - ShadowScaleLog2); | |
| 38 constexpr int32_t StackPoisonVal = -1; | |
| 34 constexpr const char *ASanPrefix = "__asan"; | 39 constexpr const char *ASanPrefix = "__asan"; |
| 35 constexpr SizeT RzSize = 32; | |
| 36 constexpr const char *RzPrefix = "__$rz"; | 40 constexpr const char *RzPrefix = "__$rz"; |
| 37 constexpr const char *RzArrayName = "__$rz_array"; | 41 constexpr const char *RzArrayName = "__$rz_array"; |
| 38 constexpr const char *RzSizesName = "__$rz_sizes"; | 42 constexpr const char *RzSizesName = "__$rz_sizes"; |
| 39 const llvm::NaClBitcodeRecord::RecordVector RzContents = | 43 const llvm::NaClBitcodeRecord::RecordVector RzContents = |
| 40 llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R'); | 44 llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R'); |
| 41 | 45 |
| 42 // In order to instrument the code correctly, the .pexe must not have had its | 46 // In order to instrument the code correctly, the .pexe must not have had its |
| 43 // symbols stripped. | 47 // symbols stripped. |
| 44 using StringMap = std::unordered_map<std::string, std::string>; | 48 using StringMap = std::unordered_map<std::string, std::string>; |
| 45 using StringSet = std::unordered_set<std::string>; | 49 using StringSet = std::unordered_set<std::string>; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 56 for (unsigned i = 0; i < sizeof(Size); ++i) { | 60 for (unsigned i = 0; i < sizeof(Size); ++i) { |
| 57 SizeContents.emplace_back(Size % (1 << CHAR_BIT)); | 61 SizeContents.emplace_back(Size % (1 << CHAR_BIT)); |
| 58 Size >>= CHAR_BIT; | 62 Size >>= CHAR_BIT; |
| 59 } | 63 } |
| 60 return SizeContents; | 64 return SizeContents; |
| 61 } | 65 } |
| 62 | 66 |
| 63 } // end of anonymous namespace | 67 } // end of anonymous namespace |
| 64 | 68 |
| 65 ICE_TLS_DEFINE_FIELD(VarSizeMap *, ASanInstrumentation, LocalVars); | 69 ICE_TLS_DEFINE_FIELD(VarSizeMap *, ASanInstrumentation, LocalVars); |
| 66 ICE_TLS_DEFINE_FIELD(std::vector<InstCall *> *, ASanInstrumentation, | 70 ICE_TLS_DEFINE_FIELD(std::vector<InstStore *> *, ASanInstrumentation, |
| 67 LocalDtors); | 71 LocalDtors); |
| 68 | 72 |
| 69 bool ASanInstrumentation::isInstrumentable(Cfg *Func) { | 73 bool ASanInstrumentation::isInstrumentable(Cfg *Func) { |
| 70 std::string FuncName = Func->getFunctionName().toStringOrEmpty(); | 74 std::string FuncName = Func->getFunctionName().toStringOrEmpty(); |
| 71 return FuncName == "" || | 75 return FuncName == "" || |
| 72 (FuncBlackList.count(FuncName) == 0 && FuncName.find(ASanPrefix) != 0); | 76 (FuncBlackList.count(FuncName) == 0 && FuncName.find(ASanPrefix) != 0); |
| 73 } | 77 } |
| 74 | 78 |
| 75 // Create redzones around all global variables, ensuring that the initializer | 79 // Create redzones around all global variables, ensuring that the initializer |
| 76 // types of the redzones and their associated globals match so that they are | 80 // types of the redzones and their associated globals match so that they are |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 std::string ASanInstrumentation::nextRzName() { | 158 std::string ASanInstrumentation::nextRzName() { |
| 155 std::stringstream Name; | 159 std::stringstream Name; |
| 156 Name << RzPrefix << RzNum++; | 160 Name << RzPrefix << RzNum++; |
| 157 return Name.str(); | 161 return Name.str(); |
| 158 } | 162 } |
| 159 | 163 |
| 160 // Check for an alloca signaling the presence of local variables and add a | 164 // Check for an alloca signaling the presence of local variables and add a |
| 161 // redzone if it is found | 165 // redzone if it is found |
| 162 void ASanInstrumentation::instrumentFuncStart(LoweringContext &Context) { | 166 void ASanInstrumentation::instrumentFuncStart(LoweringContext &Context) { |
| 163 if (ICE_TLS_GET_FIELD(LocalDtors) == nullptr) { | 167 if (ICE_TLS_GET_FIELD(LocalDtors) == nullptr) { |
| 164 ICE_TLS_SET_FIELD(LocalDtors, new std::vector<InstCall *>()); | 168 ICE_TLS_SET_FIELD(LocalDtors, new std::vector<InstStore *>()); |
| 165 ICE_TLS_SET_FIELD(LocalVars, new VarSizeMap()); | 169 ICE_TLS_SET_FIELD(LocalVars, new VarSizeMap()); |
| 166 } | 170 } |
| 167 Cfg *Func = Context.getNode()->getCfg(); | 171 Cfg *Func = Context.getNode()->getCfg(); |
| 168 bool HasLocals = false; | 172 using Entry = std::pair<SizeT, int32_t>; |
| 169 LoweringContext C; | 173 std::vector<Entry> PoisonVals; |
| 170 C.init(Context.getNode()); | 174 Variable *FirstShadowLocVar; |
| 171 std::vector<Inst *> Initializations; | 175 InstArithmetic *ShadowIndexCalc; |
| 172 Constant *InitFunc = | 176 InstArithmetic *ShadowLocCalc; |
| 173 Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_poison")); | |
| 174 Constant *DestroyFunc = | |
| 175 Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_unpoison")); | |
| 176 | |
| 177 InstAlloca *Cur; | 177 InstAlloca *Cur; |
| 178 ConstantInteger32 *VarSizeOp; | 178 ConstantInteger32 *VarSizeOp; |
| 179 while ( | 179 while ( |
| 180 (Cur = llvm::dyn_cast<InstAlloca>(iteratorToInst(C.getCur()))) && | 180 (Cur = llvm::dyn_cast<InstAlloca>(iteratorToInst(Context.getCur()))) && |
| 181 (VarSizeOp = llvm::dyn_cast<ConstantInteger32>(Cur->getSizeInBytes()))) { | 181 (VarSizeOp = llvm::dyn_cast<ConstantInteger32>(Cur->getSizeInBytes()))) { |
|
Karl
2016/08/01 15:40:21
Doesn't this code assume that all Allocas are at t
tlively
2016/08/01 17:08:50
I believe that allocas of known size corresponding
Karl
2016/08/04 14:26:34
I don't know the answer to this. I was asking beca
Jim Stichnoth
2016/08/04 14:42:54
Good point Karl. All the fixed-size alloca instru
tlively
2016/08/05 18:24:52
Done.
| |
| 182 HasLocals = true; | 182 Cur->setDeleted(); |
| 183 | |
| 184 if (PoisonVals.empty()) { | |
| 185 // insert leftmost redzone | |
| 186 Variable *LastRzVar = Func->makeVariable(IceType_i32); | |
|
Jim Stichnoth
2016/08/01 20:38:00
auto * ?
(you used it below for ShadowIndexVar)
tlively
2016/08/05 18:24:52
Done.
| |
| 187 LastRzVar->setName(Func, nextRzName()); | |
| 188 auto *ByteCount = ConstantInteger32::create(Ctx, IceType_i32, RzSize); | |
| 189 constexpr SizeT Alignment = 8; | |
| 190 Context.insert(InstAlloca::create(Func, LastRzVar, ByteCount, Alignment)); | |
| 191 PoisonVals.emplace_back(Entry{RzSize >> ShadowScaleLog2, StackPoisonVal}); | |
| 192 | |
| 193 // Calculate starting address for poisoning | |
| 194 FirstShadowLocVar = Func->makeVariable(IceType_i32); | |
| 195 FirstShadowLocVar->setName(Func, "firstShadowLoc"); | |
| 196 auto *ShadowIndexVar = Func->makeVariable(IceType_i32); | |
| 197 ShadowIndexVar->setName(Func, "shadowIndex"); | |
| 198 | |
| 199 auto *ShadowScaleLog2Const = | |
| 200 ConstantInteger32::create(Ctx, IceType_i32, ShadowScaleLog2); | |
| 201 auto *ShadowMemLocConst = | |
| 202 ConstantInteger32::create(Ctx, IceType_i32, ShadowLength32); | |
| 203 | |
| 204 ShadowIndexCalc = | |
| 205 InstArithmetic::create(Func, InstArithmetic::Lshr, ShadowIndexVar, | |
| 206 LastRzVar, ShadowScaleLog2Const); | |
| 207 ShadowLocCalc = | |
| 208 InstArithmetic::create(Func, InstArithmetic::Add, FirstShadowLocVar, | |
| 209 ShadowIndexVar, ShadowMemLocConst); | |
| 210 } | |
| 183 | 211 |
| 184 // create the new alloca that includes a redzone | 212 // create the new alloca that includes a redzone |
| 185 SizeT VarSize = VarSizeOp->getValue(); | 213 SizeT VarSize = VarSizeOp->getValue(); |
| 186 Variable *Dest = Cur->getDest(); | 214 Variable *Dest = Cur->getDest(); |
| 187 ICE_TLS_GET_FIELD(LocalVars)->insert({Dest, VarSize}); | 215 ICE_TLS_GET_FIELD(LocalVars)->insert({Dest, VarSize}); |
| 188 SizeT RzPadding = RzSize + Utils::OffsetToAlignment(VarSize, RzSize); | 216 SizeT RzPadding = RzSize + Utils::OffsetToAlignment(VarSize, RzSize); |
| 189 auto *ByteCount = | 217 auto *ByteCount = |
| 190 ConstantInteger32::create(Ctx, IceType_i32, VarSize + RzPadding); | 218 ConstantInteger32::create(Ctx, IceType_i32, VarSize + RzPadding); |
| 191 constexpr SizeT Alignment = 8; | 219 constexpr SizeT Alignment = 8; |
| 192 auto *NewVar = InstAlloca::create(Func, Dest, ByteCount, Alignment); | 220 auto *NewVar = InstAlloca::create(Func, Dest, ByteCount, Alignment); |
|
Jim Stichnoth
2016/08/01 20:38:00
Could you name this something like "NewAllocaVar"
tlively
2016/08/05 18:24:52
Done.
| |
| 221 Context.insert(NewVar); | |
| 193 | 222 |
| 194 // calculate the redzone offset | 223 SizeT Zeros = VarSize >> ShadowScaleLog2; |
|
Jim Stichnoth
2016/08/01 20:38:01
Declare scalar locals as const when possible/pract
tlively
2016/08/05 18:24:52
Done.
| |
| 195 Variable *RzLocVar = Func->makeVariable(IceType_i32); | 224 SizeT Offset = VarSize % ShadowScale; |
| 196 RzLocVar->setName(Func, nextRzName()); | 225 SizeT PoisonBytes = ((VarSize + RzPadding) >> ShadowScaleLog2) - Zeros - 1; |
| 197 auto *Offset = ConstantInteger32::create(Ctx, IceType_i32, VarSize); | 226 if (Zeros > 0) |
| 198 auto *RzLoc = InstArithmetic::create(Func, InstArithmetic::Add, RzLocVar, | 227 PoisonVals.emplace_back(Entry{Zeros, 0}); |
| 199 Dest, Offset); | 228 PoisonVals.emplace_back(Entry{1, (Offset == 0) ? StackPoisonVal : Offset}); |
| 200 | 229 PoisonVals.emplace_back(Entry{PoisonBytes, StackPoisonVal}); |
| 201 // instructions to poison and unpoison the redzone | 230 Context.advanceCur(); |
| 202 constexpr SizeT NumArgs = 2; | 231 Context.advanceNext(); |
| 203 constexpr Variable *Void = nullptr; | |
| 204 constexpr bool NoTailcall = false; | |
| 205 auto *Init = InstCall::create(Func, NumArgs, Void, InitFunc, NoTailcall); | |
| 206 auto *Destroy = | |
| 207 InstCall::create(Func, NumArgs, Void, DestroyFunc, NoTailcall); | |
| 208 Init->addArg(RzLocVar); | |
| 209 Destroy->addArg(RzLocVar); | |
| 210 auto *RzSizeConst = ConstantInteger32::create(Ctx, IceType_i32, RzPadding); | |
| 211 Init->addArg(RzSizeConst); | |
| 212 Destroy->addArg(RzSizeConst); | |
| 213 | |
| 214 Cur->setDeleted(); | |
| 215 C.insert(NewVar); | |
| 216 ICE_TLS_GET_FIELD(LocalDtors)->emplace_back(Destroy); | |
| 217 Initializations.emplace_back(RzLoc); | |
| 218 Initializations.emplace_back(Init); | |
| 219 | |
| 220 C.advanceCur(); | |
| 221 C.advanceNext(); | |
| 222 } | 232 } |
| 223 | 233 |
| 224 C.setInsertPoint(C.getCur()); | 234 if (PoisonVals.empty()) |
| 235 return; | |
| 225 | 236 |
| 226 // add the leftmost redzone | 237 Context.setNext(Context.getCur()); |
| 227 if (HasLocals) { | 238 Context.insert(ShadowIndexCalc); |
| 228 Variable *LastRz = Func->makeVariable(IceType_i32); | 239 Context.insert(ShadowLocCalc); |
| 229 LastRz->setName(Func, nextRzName()); | |
| 230 auto *ByteCount = ConstantInteger32::create(Ctx, IceType_i32, RzSize); | |
| 231 constexpr SizeT Alignment = 8; | |
| 232 auto *RzAlloca = InstAlloca::create(Func, LastRz, ByteCount, Alignment); | |
| 233 | 240 |
| 234 constexpr SizeT NumArgs = 2; | 241 // Poison redzones |
| 235 constexpr Variable *Void = nullptr; | 242 std::vector<Entry>::iterator Iter = PoisonVals.begin(); |
| 236 constexpr bool NoTailcall = false; | 243 for (SizeT Offset = 0; Iter != PoisonVals.end(); Offset += 4) { |
|
Jim Stichnoth
2016/08/01 20:38:01
Instead of magic constant "4", can you make it a s
tlively
2016/08/05 18:24:52
Done.
| |
| 237 auto *Init = InstCall::create(Func, NumArgs, Void, InitFunc, NoTailcall); | 244 int32_t CurVals[4] = {0}; |
| 238 auto *Destroy = | 245 for (int I = 0; I < 4; ++I) { |
|
Jim Stichnoth
2016/08/01 20:38:01
Can you use int32_t or uint32_t instead of int her
tlively
2016/08/05 18:24:53
Done.
| |
| 239 InstCall::create(Func, NumArgs, Void, DestroyFunc, NoTailcall); | 246 if (Iter == PoisonVals.end()) |
| 240 Init->addArg(LastRz); | 247 break; |
| 241 Destroy->addArg(LastRz); | 248 Entry Val = *Iter; |
| 242 Init->addArg(RzAlloca->getSizeInBytes()); | 249 CurVals[I] = Val.second; |
| 243 Destroy->addArg(RzAlloca->getSizeInBytes()); | 250 --Val.first; |
| 244 | 251 if (Val.first > 0) |
| 245 ICE_TLS_GET_FIELD(LocalDtors)->emplace_back(Destroy); | 252 *Iter = Val; |
| 246 C.insert(RzAlloca); | 253 else |
| 247 C.insert(Init); | 254 ++Iter; |
| 255 } | |
| 256 int32_t Poison = ((CurVals[3] & 0xff) << 24) | ((CurVals[2] & 0xff) << 16) | | |
| 257 ((CurVals[1] & 0xff) << 8) | (CurVals[0] & 0xff); | |
| 258 if (Poison == 0) | |
| 259 continue; | |
| 260 auto *PoisonConst = ConstantInteger32::create(Ctx, IceType_i32, Poison); | |
| 261 auto *ZeroConst = ConstantInteger32::create(Ctx, IceType_i32, 0); | |
| 262 auto *OffsetConst = ConstantInteger32::create(Ctx, IceType_i32, Offset); | |
| 263 auto *PoisonAddrVar = Func->makeVariable(IceType_i32); | |
| 264 Context.insert(InstArithmetic::create(Func, InstArithmetic::Add, | |
| 265 PoisonAddrVar, FirstShadowLocVar, | |
| 266 OffsetConst)); | |
| 267 Context.insert(InstStore::create(Func, PoisonConst, PoisonAddrVar)); | |
| 268 ICE_TLS_GET_FIELD(LocalDtors) | |
| 269 ->emplace_back(InstStore::create(Func, ZeroConst, PoisonAddrVar)); | |
| 248 } | 270 } |
| 249 | 271 Context.advanceCur(); |
| 250 // insert initializers for the redzones | 272 Context.advanceNext(); |
| 251 for (Inst *Init : Initializations) { | |
| 252 C.insert(Init); | |
| 253 } | |
| 254 } | 273 } |
| 255 | 274 |
| 256 void ASanInstrumentation::instrumentCall(LoweringContext &Context, | 275 void ASanInstrumentation::instrumentCall(LoweringContext &Context, |
| 257 InstCall *Instr) { | 276 InstCall *Instr) { |
| 258 auto *CallTarget = | 277 auto *CallTarget = |
| 259 llvm::dyn_cast<ConstantRelocatable>(Instr->getCallTarget()); | 278 llvm::dyn_cast<ConstantRelocatable>(Instr->getCallTarget()); |
| 260 if (CallTarget == nullptr) | 279 if (CallTarget == nullptr) |
| 261 return; | 280 return; |
| 262 | 281 |
| 263 std::string TargetName = CallTarget->getName().toStringOrEmpty(); | 282 std::string TargetName = CallTarget->getName().toStringOrEmpty(); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 320 auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Op); | 339 auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Op); |
| 321 if (Reloc == nullptr) | 340 if (Reloc == nullptr) |
| 322 return false; | 341 return false; |
| 323 RelocOffsetT Offset = Reloc->getOffset(); | 342 RelocOffsetT Offset = Reloc->getOffset(); |
| 324 GlobalSizeMap::iterator GlobalSize = GlobalSizes.find(Reloc->getName()); | 343 GlobalSizeMap::iterator GlobalSize = GlobalSizes.find(Reloc->getName()); |
| 325 return GlobalSize != GlobalSizes.end() && GlobalSize->second - Offset >= Size; | 344 return GlobalSize != GlobalSizes.end() && GlobalSize->second - Offset >= Size; |
| 326 } | 345 } |
| 327 | 346 |
| 328 void ASanInstrumentation::instrumentRet(LoweringContext &Context, InstRet *) { | 347 void ASanInstrumentation::instrumentRet(LoweringContext &Context, InstRet *) { |
| 329 Cfg *Func = Context.getNode()->getCfg(); | 348 Cfg *Func = Context.getNode()->getCfg(); |
| 330 InstList::iterator Next = Context.getNext(); | |
| 331 Context.setInsertPoint(Context.getCur()); | 349 Context.setInsertPoint(Context.getCur()); |
| 332 for (InstCall *RzUnpoison : *ICE_TLS_GET_FIELD(LocalDtors)) { | 350 for (InstStore *RzUnpoison : *ICE_TLS_GET_FIELD(LocalDtors)) { |
| 333 SizeT NumArgs = RzUnpoison->getNumArgs(); | 351 Context.insert( |
| 334 Variable *Dest = RzUnpoison->getDest(); | 352 InstStore::create(Func, RzUnpoison->getData(), RzUnpoison->getAddr())); |
| 335 Operand *CallTarget = RzUnpoison->getCallTarget(); | |
| 336 bool HasTailCall = RzUnpoison->isTailcall(); | |
| 337 bool IsTargetHelperCall = RzUnpoison->isTargetHelperCall(); | |
| 338 auto *RzUnpoisonCpy = InstCall::create(Func, NumArgs, Dest, CallTarget, | |
| 339 HasTailCall, IsTargetHelperCall); | |
| 340 for (int I = 0, Args = RzUnpoison->getNumArgs(); I < Args; ++I) { | |
| 341 RzUnpoisonCpy->addArg(RzUnpoison->getArg(I)); | |
| 342 } | |
| 343 Context.insert(RzUnpoisonCpy); | |
| 344 } | 353 } |
| 345 Context.setNext(Next); | 354 Context.advanceCur(); |
| 355 Context.advanceNext(); | |
| 346 } | 356 } |
| 347 | 357 |
| 348 void ASanInstrumentation::instrumentStart(Cfg *Func) { | 358 void ASanInstrumentation::instrumentStart(Cfg *Func) { |
| 349 Constant *ShadowMemInit = | 359 Constant *ShadowMemInit = |
| 350 Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_init")); | 360 Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_init")); |
| 351 constexpr SizeT NumArgs = 3; | 361 constexpr SizeT NumArgs = 3; |
| 352 constexpr Variable *Void = nullptr; | 362 constexpr Variable *Void = nullptr; |
| 353 constexpr bool NoTailCall = false; | 363 constexpr bool NoTailCall = false; |
| 354 auto *Call = InstCall::create(Func, NumArgs, Void, ShadowMemInit, NoTailCall); | 364 auto *Call = InstCall::create(Func, NumArgs, Void, ShadowMemInit, NoTailCall); |
| 355 Func->getEntryNode()->getInsts().push_front(Call); | 365 Func->getEntryNode()->getInsts().push_front(Call); |
| 356 | 366 |
| 357 instrumentGlobals(*getGlobals()); | 367 instrumentGlobals(*getGlobals()); |
| 358 | 368 |
| 359 Call->addArg(ConstantInteger32::create(Ctx, IceType_i32, RzGlobalsNum)); | 369 Call->addArg(ConstantInteger32::create(Ctx, IceType_i32, RzGlobalsNum)); |
| 360 Call->addArg(Ctx->getConstantSym(0, Ctx->getGlobalString(RzArrayName))); | 370 Call->addArg(Ctx->getConstantSym(0, Ctx->getGlobalString(RzArrayName))); |
| 361 Call->addArg(Ctx->getConstantSym(0, Ctx->getGlobalString(RzSizesName))); | 371 Call->addArg(Ctx->getConstantSym(0, Ctx->getGlobalString(RzSizesName))); |
| 362 } | 372 } |
| 363 | 373 |
| 364 // TODO(tlively): make this more efficient with swap idiom | 374 // TODO(tlively): make this more efficient with swap idiom |
| 365 void ASanInstrumentation::finishFunc(Cfg *) { | 375 void ASanInstrumentation::finishFunc(Cfg *) { |
| 366 ICE_TLS_GET_FIELD(LocalVars)->clear(); | 376 ICE_TLS_GET_FIELD(LocalVars)->clear(); |
| 367 ICE_TLS_GET_FIELD(LocalDtors)->clear(); | 377 ICE_TLS_GET_FIELD(LocalDtors)->clear(); |
| 368 } | 378 } |
| 369 | 379 |
| 370 } // end of namespace Ice | 380 } // end of namespace Ice |
| OLD | NEW |