461 lines
23 KiB
C++
461 lines
23 KiB
C++
//===- subzero/unittest/AssemblerX8632/Locked.cpp -------------------------===//
|
|
//
|
|
// The Subzero Code Generator
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "AssemblerX8632/TestUtil.h"
|
|
|
|
namespace Ice {
|
|
namespace X8632 {
|
|
namespace Test {
|
|
namespace {
|
|
|
|
TEST_F(AssemblerX8632LowLevelTest, Mfence) {
|
|
__ mfence();
|
|
|
|
static constexpr uint8_t ByteCount = 3;
|
|
ASSERT_EQ(ByteCount, codeBytesSize());
|
|
verifyBytes<ByteCount>(codeBytes(), 0x0F, 0xAE, 0xF0);
|
|
}
|
|
|
|
TEST_F(AssemblerX8632LowLevelTest, Lock) {
|
|
__ lock();
|
|
|
|
static constexpr uint8_t ByteCount = 1;
|
|
ASSERT_EQ(ByteCount, codeBytesSize());
|
|
verifyBytes<ByteCount>(codeBytes(), 0xF0);
|
|
}
|
|
|
|
TEST_F(AssemblerX8632Test, Xchg) {
|
|
static constexpr uint32_t Mask8 = 0x000000FF;
|
|
static constexpr uint32_t Mask16 = 0x0000FFFF;
|
|
static constexpr uint32_t Mask32 = 0xFFFFFFFF;
|
|
|
|
#define TestImplAddrReg(Value0, Dst1, Value1, Size) \
|
|
do { \
|
|
static constexpr char TestString[] = \
|
|
"(" #Value0 ", " #Dst1 ", " #Value1 ", " #Size ")"; \
|
|
const uint32_t T0 = allocateDword(); \
|
|
const uint32_t V0 = (Value0)&Mask##Size; \
|
|
const uint32_t V1 = (Value1)&Mask##Size; \
|
|
\
|
|
__ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
|
|
Immediate(Value1)); \
|
|
__ xchg(IceType_i##Size, dwordAddress(T0), \
|
|
GPRRegister::Encoded_Reg_##Dst1); \
|
|
__ And(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \
|
|
Immediate(Mask##Size)); \
|
|
\
|
|
AssembledTest test = assemble(); \
|
|
test.setDwordTo(T0, V0); \
|
|
test.run(); \
|
|
\
|
|
ASSERT_EQ(V0, test.Dst1()) << TestString; \
|
|
ASSERT_EQ(V1, test.contentsOfDword(T0)) << TestString; \
|
|
reset(); \
|
|
} while (0)
|
|
|
|
#define TestImplSize(Dst1, Size) \
|
|
do { \
|
|
TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, Size); \
|
|
} while (0)
|
|
|
|
#define TestImpl(Dst1) \
|
|
do { \
|
|
if (GPRRegister::Encoded_Reg_##Dst1 < 4) { \
|
|
TestImplSize(Dst1, 8); \
|
|
} \
|
|
TestImplSize(Dst1, 16); \
|
|
TestImplSize(Dst1, 32); \
|
|
} while (0)
|
|
|
|
TestImpl(eax);
|
|
TestImpl(ebx);
|
|
TestImpl(ecx);
|
|
TestImpl(edx);
|
|
TestImpl(esi);
|
|
TestImpl(edi);
|
|
|
|
#undef TestImpl
|
|
#undef TestImplSize
|
|
#undef TestImplAddrReg
|
|
|
|
#define TestImplRegReg(Reg0, Value0, Reg1, Value1, Size) \
|
|
do { \
|
|
static constexpr char TestString[] = \
|
|
"(" #Reg0 "," #Value0 ", " #Reg1 ", " #Value1 ", " #Size ")"; \
|
|
const uint32_t V0 = (Value0)&Mask##Size; \
|
|
const uint32_t V1 = (Value1)&Mask##Size; \
|
|
\
|
|
__ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Reg0, \
|
|
Immediate(Value0)); \
|
|
__ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Reg1, \
|
|
Immediate(Value1)); \
|
|
__ xchg(IceType_i##Size, GPRRegister::Encoded_Reg_##Reg0, \
|
|
GPRRegister::Encoded_Reg_##Reg1); \
|
|
__ And(IceType_i32, GPRRegister::Encoded_Reg_##Reg0, \
|
|
Immediate(Mask##Size)); \
|
|
__ And(IceType_i32, GPRRegister::Encoded_Reg_##Reg1, \
|
|
Immediate(Mask##Size)); \
|
|
\
|
|
AssembledTest test = assemble(); \
|
|
test.run(); \
|
|
\
|
|
ASSERT_EQ(V0, test.Reg1()) << TestString; \
|
|
ASSERT_EQ(V1, test.Reg0()) << TestString; \
|
|
reset(); \
|
|
} while (0)
|
|
|
|
#define TestImplSize(Reg0, Reg1, Size) \
|
|
do { \
|
|
TestImplRegReg(Reg0, 0xa2b34567, Reg1, 0x0507ddee, Size); \
|
|
} while (0)
|
|
|
|
#define TestImpl(Reg0, Reg1) \
|
|
do { \
|
|
if (GPRRegister::Encoded_Reg_##Reg0 < 4 && \
|
|
GPRRegister::Encoded_Reg_##Reg1 < 4) { \
|
|
TestImplSize(Reg0, Reg1, 8); \
|
|
} \
|
|
TestImplSize(Reg0, Reg1, 16); \
|
|
TestImplSize(Reg0, Reg1, 32); \
|
|
} while (0)
|
|
|
|
TestImpl(eax, ebx);
|
|
TestImpl(edx, eax);
|
|
TestImpl(ecx, edx);
|
|
TestImpl(esi, eax);
|
|
TestImpl(edx, edi);
|
|
|
|
#undef TestImpl
|
|
#undef TestImplSize
|
|
#undef TestImplRegReg
|
|
}
|
|
|
|
TEST_F(AssemblerX8632Test, Xadd) {
|
|
static constexpr bool NotLocked = false;
|
|
static constexpr bool Locked = true;
|
|
|
|
static constexpr uint32_t Mask8 = 0x000000FF;
|
|
static constexpr uint32_t Mask16 = 0x0000FFFF;
|
|
static constexpr uint32_t Mask32 = 0xFFFFFFFF;
|
|
|
|
#define TestImplAddrReg(Value0, Dst1, Value1, LockedOrNot, Size) \
|
|
do { \
|
|
static constexpr char TestString[] = \
|
|
"(" #Value0 ", " #Dst1 ", " #Value1 ", " #Size ")"; \
|
|
const uint32_t T0 = allocateDword(); \
|
|
const uint32_t V0 = (Value0)&Mask##Size; \
|
|
const uint32_t V1 = (Value1)&Mask##Size; \
|
|
\
|
|
__ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
|
|
Immediate(Value1)); \
|
|
__ xadd(IceType_i##Size, dwordAddress(T0), \
|
|
GPRRegister::Encoded_Reg_##Dst1, LockedOrNot); \
|
|
__ And(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \
|
|
Immediate(Mask##Size)); \
|
|
\
|
|
AssembledTest test = assemble(); \
|
|
test.setDwordTo(T0, V0); \
|
|
test.run(); \
|
|
\
|
|
ASSERT_EQ(V0, test.Dst1()) << TestString; \
|
|
ASSERT_EQ(Mask##Size &(V1 + V0), test.contentsOfDword(T0)) << TestString; \
|
|
reset(); \
|
|
} while (0)
|
|
|
|
#define TestImplSize(Dst1, Size) \
|
|
do { \
|
|
TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, NotLocked, Size); \
|
|
TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, Locked, Size); \
|
|
} while (0)
|
|
|
|
#define TestImpl(Dst1) \
|
|
do { \
|
|
if (GPRRegister::Encoded_Reg_##Dst1 < 4) { \
|
|
TestImplSize(Dst1, 8); \
|
|
} \
|
|
TestImplSize(Dst1, 16); \
|
|
TestImplSize(Dst1, 32); \
|
|
} while (0)
|
|
|
|
TestImpl(eax);
|
|
TestImpl(ebx);
|
|
TestImpl(ecx);
|
|
TestImpl(edx);
|
|
TestImpl(esi);
|
|
TestImpl(edi);
|
|
|
|
#undef TestImpl
|
|
#undef TestImplSize
|
|
#undef TestImplAddrReg
|
|
}
|
|
|
|
TEST_F(AssemblerX8632LowLevelTest, Xadd) {
|
|
static constexpr bool NotLocked = false;
|
|
static constexpr bool Locked = true;
|
|
|
|
// Ensures that xadd emits a lock prefix accordingly.
|
|
{
|
|
__ xadd(IceType_i8, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked8 = 7;
|
|
ASSERT_EQ(ByteCountNotLocked8, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked8>(codeBytes(), 0x0F, 0xC0, 0x35, 0x00, 0xFF,
|
|
0x01, 0x00);
|
|
reset();
|
|
|
|
__ xadd(IceType_i8, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, Locked);
|
|
static constexpr uint8_t ByteCountLocked8 = 1 + ByteCountNotLocked8;
|
|
ASSERT_EQ(ByteCountLocked8, codeBytesSize());
|
|
verifyBytes<ByteCountLocked8>(codeBytes(), 0xF0, 0x0F, 0xC0, 0x35, 0x00,
|
|
0xFF, 0x01, 0x00);
|
|
reset();
|
|
}
|
|
|
|
{
|
|
__ xadd(IceType_i16, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked16 = 8;
|
|
ASSERT_EQ(ByteCountNotLocked16, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked16>(codeBytes(), 0x66, 0x0F, 0xC1, 0x35, 0x00,
|
|
0xFF, 0x01, 0x00);
|
|
reset();
|
|
|
|
__ xadd(IceType_i16, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, Locked);
|
|
static constexpr uint8_t ByteCountLocked16 = 1 + ByteCountNotLocked16;
|
|
ASSERT_EQ(ByteCountLocked16, codeBytesSize());
|
|
verifyBytes<ByteCountLocked16>(codeBytes(), 0x66, 0xF0, 0x0F, 0xC1, 0x35,
|
|
0x00, 0xFF, 0x01, 0x00);
|
|
reset();
|
|
}
|
|
|
|
{
|
|
__ xadd(IceType_i32, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked32 = 7;
|
|
ASSERT_EQ(ByteCountNotLocked32, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked32>(codeBytes(), 0x0F, 0xC1, 0x35, 0x00, 0xFF,
|
|
0x01, 0x00);
|
|
reset();
|
|
|
|
__ xadd(IceType_i32, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, Locked);
|
|
static constexpr uint8_t ByteCountLocked32 = 1 + ByteCountNotLocked32;
|
|
ASSERT_EQ(ByteCountLocked32, codeBytesSize());
|
|
verifyBytes<ByteCountLocked32>(codeBytes(), 0xF0, 0x0F, 0xC1, 0x35, 0x00,
|
|
0xFF, 0x01, 0x00);
|
|
reset();
|
|
}
|
|
}
|
|
|
|
TEST_F(AssemblerX8632Test, Cmpxchg8b) {
|
|
static constexpr bool NotLocked = false;
|
|
static constexpr bool Locked = true;
|
|
|
|
#define TestImpl(Value0, Value1, ValueMem, LockedOrNot) \
|
|
do { \
|
|
static constexpr char TestString[] = \
|
|
"(" #Value0 ", " #Value1 ", " #ValueMem ", " #LockedOrNot ")"; \
|
|
const uint32_t T0 = allocateQword(); \
|
|
static constexpr uint64_t V0 = ValueMem; \
|
|
const uint32_t ZeroFlag = allocateDword(); \
|
|
\
|
|
__ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, \
|
|
Immediate(uint64_t(Value0) & 0xFFFFFFFF)); \
|
|
__ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, \
|
|
Immediate(uint64_t(Value0) >> 32)); \
|
|
__ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, \
|
|
Immediate(uint64_t(Value1) & 0xFFFFFFFF)); \
|
|
__ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, \
|
|
Immediate(uint64_t(Value1) >> 32)); \
|
|
__ cmpxchg8b(dwordAddress(T0), LockedOrNot); \
|
|
__ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \
|
|
\
|
|
AssembledTest test = assemble(); \
|
|
test.setQwordTo(T0, V0); \
|
|
test.setDwordTo(ZeroFlag, uint32_t(0xFF)); \
|
|
test.run(); \
|
|
\
|
|
if (V0 == (Value0)) { \
|
|
ASSERT_EQ(uint64_t(Value1), test.contentsOfQword(T0)) << TestString; \
|
|
ASSERT_EQ(1u, test.contentsOfDword(ZeroFlag)) << TestString; \
|
|
} else { \
|
|
ASSERT_EQ(uint64_t(ValueMem) & 0xFFFFFFFF, test.eax()) << TestString; \
|
|
ASSERT_EQ((uint64_t(ValueMem) >> 32) & 0xFFFFFFFF, test.edx()) \
|
|
<< TestString; \
|
|
ASSERT_EQ(0u, test.contentsOfDword(ZeroFlag)) << TestString; \
|
|
} \
|
|
reset(); \
|
|
} while (0)
|
|
|
|
TestImpl(0x98987676543210ull, 0x1, 0x98987676543210ull, NotLocked);
|
|
TestImpl(0x98987676543210ull, 0x1, 0x98987676543210ull, Locked);
|
|
TestImpl(0x98987676543210ull, 0x1, 0x98987676543211ull, NotLocked);
|
|
TestImpl(0x98987676543210ull, 0x1, 0x98987676543211ull, Locked);
|
|
|
|
#undef TestImpl
|
|
}
|
|
|
|
TEST_F(AssemblerX8632LowLevelTest, Cmpxchg8b) {
|
|
static constexpr bool NotLocked = false;
|
|
static constexpr bool Locked = true;
|
|
|
|
// Ensures that cmpxchg8b emits a lock prefix accordingly.
|
|
__ cmpxchg8b(Address(0x1FF00, AssemblerFixup::NoFixup), NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked = 7;
|
|
ASSERT_EQ(ByteCountNotLocked, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked>(codeBytes(), 0x0F, 0xC7, 0x0D, 0x00, 0xFF,
|
|
0x01, 0x00);
|
|
reset();
|
|
|
|
__ cmpxchg8b(Address(0x1FF00, AssemblerFixup::NoFixup), Locked);
|
|
static constexpr uint8_t ByteCountLocked = 1 + ByteCountNotLocked;
|
|
ASSERT_EQ(ByteCountLocked, codeBytesSize());
|
|
verifyBytes<ByteCountLocked>(codeBytes(), 0xF0, 0x0F, 0xC7, 0x0D, 0x00, 0xFF,
|
|
0x01, 0x00);
|
|
reset();
|
|
}
|
|
|
|
TEST_F(AssemblerX8632Test, Cmpxchg) {
|
|
static constexpr bool NotLocked = false;
|
|
static constexpr bool Locked = true;
|
|
|
|
static constexpr uint32_t Mask8 = 0x000000FF;
|
|
static constexpr uint32_t Mask16 = 0x0000FFFF;
|
|
static constexpr uint32_t Mask32 = 0xFFFFFFFF;
|
|
|
|
#define TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, Size) \
|
|
do { \
|
|
static constexpr char TestString[] = \
|
|
"(" #Value0 ", " #Src ", " #Value1 ", " #ValueMem ", " #LockedOrNot \
|
|
", " #Size ")"; \
|
|
const uint32_t T0 = allocateDword(); \
|
|
static constexpr uint32_t V0 = (ValueMem)&Mask##Size; \
|
|
const uint32_t ZeroFlag = allocateDword(); \
|
|
\
|
|
__ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
|
|
Immediate((Value0)&Mask##Size)); \
|
|
__ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
|
|
Immediate((Value1)&Mask##Size)); \
|
|
__ cmpxchg(IceType_i##Size, dwordAddress(T0), \
|
|
GPRRegister::Encoded_Reg_##Src, LockedOrNot); \
|
|
__ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \
|
|
\
|
|
AssembledTest test = assemble(); \
|
|
test.setDwordTo(T0, V0); \
|
|
test.setDwordTo(ZeroFlag, uint32_t(0xFF)); \
|
|
test.run(); \
|
|
\
|
|
if (V0 == (Mask##Size & (Value0))) { \
|
|
ASSERT_EQ(uint32_t((Value1)&Mask##Size), test.contentsOfDword(T0)) \
|
|
<< TestString; \
|
|
ASSERT_EQ(1u, test.contentsOfDword(ZeroFlag)) << TestString; \
|
|
} else { \
|
|
ASSERT_EQ(uint32_t((ValueMem)&Mask##Size), test.eax()) << TestString; \
|
|
ASSERT_EQ(0u, test.contentsOfDword(ZeroFlag)) << TestString; \
|
|
} \
|
|
reset(); \
|
|
} while (0)
|
|
|
|
#define TestImplValue(Value0, Src, Value1, ValueMem, LockedOrNot) \
|
|
do { \
|
|
if (GPRRegister::Encoded_Reg_##Src < 4) { \
|
|
TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 8); \
|
|
} \
|
|
TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 16); \
|
|
TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 32); \
|
|
} while (0)
|
|
|
|
#define TestImpl(Src, LockedOrNot) \
|
|
do { \
|
|
TestImplValue(0xFFFFFFFF, Src, 0x1, 0xFFFFFFFF, LockedOrNot); \
|
|
TestImplValue(0x0FFF0F0F, Src, 0x1, 0xFFFFFFFF, LockedOrNot); \
|
|
} while (0)
|
|
|
|
TestImpl(ebx, Locked);
|
|
TestImpl(edx, NotLocked);
|
|
TestImpl(ecx, Locked);
|
|
TestImpl(ecx, NotLocked);
|
|
TestImpl(edx, Locked);
|
|
TestImpl(edx, NotLocked);
|
|
TestImpl(esi, Locked);
|
|
TestImpl(esi, NotLocked);
|
|
TestImpl(edi, Locked);
|
|
TestImpl(edi, NotLocked);
|
|
|
|
#undef TestImpl
|
|
#undef TestImplValue
|
|
#undef TestImplAddrReg
|
|
}
|
|
|
|
TEST_F(AssemblerX8632LowLevelTest, Cmpxchg) {
|
|
static constexpr bool NotLocked = false;
|
|
static constexpr bool Locked = true;
|
|
|
|
// Ensures that cmpxchg emits a lock prefix accordingly.
|
|
{
|
|
__ cmpxchg(IceType_i8, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked8 = 7;
|
|
ASSERT_EQ(ByteCountNotLocked8, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked8>(codeBytes(), 0x0F, 0xB0, 0x35, 0x00, 0xFF,
|
|
0x01, 0x00);
|
|
reset();
|
|
|
|
__ cmpxchg(IceType_i8, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, Locked);
|
|
static constexpr uint8_t ByteCountLocked8 = 1 + ByteCountNotLocked8;
|
|
ASSERT_EQ(ByteCountLocked8, codeBytesSize());
|
|
verifyBytes<ByteCountLocked8>(codeBytes(), 0xF0, 0x0F, 0xB0, 0x35, 0x00,
|
|
0xFF, 0x01, 0x00);
|
|
reset();
|
|
}
|
|
|
|
{
|
|
__ cmpxchg(IceType_i16, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked16 = 8;
|
|
ASSERT_EQ(ByteCountNotLocked16, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked16>(codeBytes(), 0x66, 0x0F, 0xB1, 0x35, 0x00,
|
|
0xFF, 0x01, 0x00);
|
|
reset();
|
|
|
|
__ cmpxchg(IceType_i16, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, Locked);
|
|
static constexpr uint8_t ByteCountLocked16 = 1 + ByteCountNotLocked16;
|
|
ASSERT_EQ(ByteCountLocked16, codeBytesSize());
|
|
verifyBytes<ByteCountLocked16>(codeBytes(), 0x66, 0xF0, 0x0F, 0xB1, 0x35,
|
|
0x00, 0xFF, 0x01, 0x00);
|
|
reset();
|
|
}
|
|
|
|
{
|
|
__ cmpxchg(IceType_i32, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, NotLocked);
|
|
static constexpr uint8_t ByteCountNotLocked32 = 7;
|
|
ASSERT_EQ(ByteCountNotLocked32, codeBytesSize());
|
|
verifyBytes<ByteCountNotLocked32>(codeBytes(), 0x0F, 0xB1, 0x35, 0x00, 0xFF,
|
|
0x01, 0x00);
|
|
reset();
|
|
|
|
__ cmpxchg(IceType_i32, Address(0x1FF00, AssemblerFixup::NoFixup),
|
|
GPRRegister::Encoded_Reg_esi, Locked);
|
|
static constexpr uint8_t ByteCountLocked32 = 1 + ByteCountNotLocked32;
|
|
ASSERT_EQ(ByteCountLocked32, codeBytesSize());
|
|
verifyBytes<ByteCountLocked32>(codeBytes(), 0xF0, 0x0F, 0xB1, 0x35, 0x00,
|
|
0xFF, 0x01, 0x00);
|
|
reset();
|
|
}
|
|
}
|
|
|
|
} // end of anonymous namespace
|
|
} // end of namespace Test
|
|
} // end of namespace X8632
|
|
} // end of namespace Ice
|