Change the way Vex represents architecture variants into something
more flexible. Prior to this change, the type VexSubArch effectively
imposed a total ordering on subarchitecture capabilities, which was
overly restrictive. This change moves to effectively using a bit-set,
allowing some features (instruction groups) to be supported or not
supported independently of each other.
git-svn-id: svn://svn.valgrind.org/vex/trunk@1555 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest-amd64/toIR.c b/priv/guest-amd64/toIR.c
index 6dcee7b..ab3034c 100644
--- a/priv/guest-amd64/toIR.c
+++ b/priv/guest-amd64/toIR.c
@@ -13231,14 +13231,13 @@
HChar* fName = NULL;
void* fAddr = NULL;
if (haveF2orF3(pfx)) goto decode_failure;
- switch (archinfo->subarch) {
- case VexSubArch_NONE:
- fName = "amd64g_dirtyhelper_CPUID";
- fAddr = &amd64g_dirtyhelper_CPUID;
- break;
- default:
- vpanic("disInstr(amd64)(cpuid)");
+ if (archinfo->hwcaps == 0/*baseline, == SSE2*/) {
+ fName = "amd64g_dirtyhelper_CPUID";
+ fAddr = &amd64g_dirtyhelper_CPUID;
}
+ else
+ vpanic("disInstr(amd64)(cpuid)");
+
vassert(fName); vassert(fAddr);
d = unsafeIRDirty_0_N ( 0/*regparms*/,
fName, fAddr, mkIRExprVec_0() );
diff --git a/priv/guest-arm/toIR.c b/priv/guest-arm/toIR.c
index 64706bf..421fade 100644
--- a/priv/guest-arm/toIR.c
+++ b/priv/guest-arm/toIR.c
@@ -194,7 +194,7 @@
vassert(vex_control.guest_chase_thresh >= 0);
vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns);
- vassert(archinfo_guest->subarch == VexSubArchARM_v4);
+ vassert(archinfo_guest->hwcaps == 0);
/* Start a new, empty extent. */
vge->n_used = 1;
diff --git a/priv/guest-ppc/toIR.c b/priv/guest-ppc/toIR.c
index 47a7622..9fe7189 100644
--- a/priv/guest-ppc/toIR.c
+++ b/priv/guest-ppc/toIR.c
@@ -5610,6 +5610,7 @@
break;
case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
+ // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
storeBE( mkexpr(EA),
@@ -5692,6 +5693,7 @@
break;
case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
+ // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
if (frA_addr != 0 || frC_addr != 0) {
vex_printf("dis_fp_arith(ppc)(instr,fsqrts)\n");
return False;
@@ -5702,6 +5704,7 @@
break;
case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
+ // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
if (frA_addr != 0 || frC_addr != 0) {
vex_printf("dis_fp_arith(ppc)(instr,fres)\n");
return False;
@@ -5761,6 +5764,7 @@
break;
case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
+ // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
if (frA_addr != 0 || frC_addr != 0) {
vex_printf("dis_fp_arith(ppc)(instr,fsqrt)\n");
return False;
@@ -5771,6 +5775,7 @@
break;
case 0x17: { // fsel (Floating Select, PPC32 p426)
+ // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
IRTemp cc = newTemp(Ity_I32);
IRTemp cc_b0 = newTemp(Ity_I32);
@@ -5805,6 +5810,7 @@
break;
case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424)
+ // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
if (frA_addr != 0 || frC_addr != 0) {
vex_printf("dis_fp_arith(ppc)(instr,frsqrte)\n");
return False;
@@ -8401,18 +8407,28 @@
DisResult dres;
UInt theInstr;
IRType ty = mode64 ? Ity_I64 : Ity_I32;
+ Bool allow_F = False;
+ Bool allow_V = False;
+ Bool allow_FX = False;
+ Bool allow_GX = False;
+ UInt hwcaps = archinfo->hwcaps;
+ Long delta;
/* What insn variants are we supporting today? */
- Bool allow_FP = archinfo->subarch == VexSubArchPPC32_FI ||
- archinfo->subarch == VexSubArchPPC32_VFI ||
- archinfo->subarch == VexSubArchPPC64_FI ||
- archinfo->subarch == VexSubArchPPC64_VFI;
-
- Bool allow_VMX = archinfo->subarch == VexSubArchPPC32_VFI ||
- archinfo->subarch == VexSubArchPPC64_VFI;
+ if (mode64) {
+ allow_F = True;
+ allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC64_V));
+ allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC64_FX));
+ allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC64_GX));
+ } else {
+ allow_F = (0 != (hwcaps & VEX_HWCAPS_PPC32_F));
+ allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC32_V));
+ allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC32_FX));
+ allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC32_GX));
+ }
/* The running delta */
- Long delta = (Long)mkSzAddr(ty, (ULong)delta64);
+ delta = (Long)mkSzAddr(ty, (ULong)delta64);
/* Set result defaults. */
dres.whatNext = Dis_Continue;
@@ -8424,7 +8440,7 @@
and have done. */
theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
-// vex_printf("insn: 0x%x\n", theInstr);
+ if (0) vex_printf("insn: 0x%x\n", theInstr);
DIP("\t0x%llx: ", (ULong)guest_CIA_curr_instr);
@@ -8579,14 +8595,14 @@
/* Floating Point Load Instructions */
case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
case 0x33: // lfdu
- if (!allow_FP) goto decode_noFP;
+ if (!allow_F) goto decode_noF;
if (dis_fp_load( theInstr )) goto decode_success;
goto decode_failure;
/* Floating Point Store Instructions */
case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
case 0x37: // stfdux
- if (!allow_FP) goto decode_noFP;
+ if (!allow_F) goto decode_noF;
if (dis_fp_store( theInstr )) goto decode_success;
goto decode_failure;
@@ -8597,16 +8613,23 @@
goto decode_failure;
case 0x3B:
- if (!allow_FP) goto decode_noFP;
-
+ if (!allow_F) goto decode_noF;
opc2 = IFIELD(theInstr, 1, 5);
switch (opc2) {
/* Floating Point Arith Instructions */
case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
- case 0x16: case 0x18: case 0x19: // fsqrts, fres, fmuls
+ case 0x19: // fmuls
if (dis_fp_arith(theInstr)) goto decode_success;
goto decode_failure;
-
+ case 0x16: // fsqrts
+ if (!allow_FX) goto decode_noFX;
+ if (dis_fp_arith(theInstr)) goto decode_success;
+ goto decode_failure;
+ case 0x18: // fres
+ if (!allow_GX) goto decode_noGX;
+ if (dis_fp_arith(theInstr)) goto decode_success;
+ goto decode_failure;
+
/* Floating Point Mult-Add Instructions */
case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
case 0x1F: // fnmadds
@@ -8625,16 +8648,23 @@
goto decode_failure;
case 0x3F:
- if (!allow_FP) goto decode_noFP;
+ if (!allow_F) goto decode_noF;
/* Instrs using opc[1:5] never overlap instrs using opc[1:10],
so we can simply fall through the first switch statement */
opc2 = IFIELD(theInstr, 1, 5);
switch (opc2) {
/* Floating Point Arith Instructions */
- case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
- case 0x16: case 0x17: case 0x19: // fsqrt, fsel, fmul
- case 0x1A: // frsqrte
+ case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
+ case 0x19: // fmul
+ if (dis_fp_arith(theInstr)) goto decode_success;
+ goto decode_failure;
+ case 0x16: // fsqrt
+ if (!allow_FX) goto decode_noFX;
+ if (dis_fp_arith(theInstr)) goto decode_success;
+ goto decode_failure;
+ case 0x17: case 0x1A: // fsel, frsqrte
+ if (!allow_GX) goto decode_noGX;
if (dis_fp_arith(theInstr)) goto decode_success;
goto decode_failure;
@@ -8868,23 +8898,27 @@
/* Floating Point Load Instructions */
case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
case 0x277: // lfdux
- if (!allow_FP) goto decode_noFP;
+ if (!allow_F) goto decode_noF;
if (dis_fp_load( theInstr )) goto decode_success;
goto decode_failure;
/* Floating Point Store Instructions */
case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
- case 0x2F7: case 0x3D7: // stfdu, stfiwx
- if (!allow_FP) goto decode_noFP;
+ case 0x2F7: // stfdu, stfiwx
+ if (!allow_F) goto decode_noF;
if (dis_fp_store( theInstr )) goto decode_success;
goto decode_failure;
-
+ case 0x3D7: // stfiwx
+ if (!allow_F) goto decode_noF;
+ if (!allow_GX) goto decode_noGX;
+ if (dis_fp_store( theInstr )) goto decode_success;
+ goto decode_failure;
/* AltiVec instructions */
/* AV Cache Control - Data streams */
case 0x156: case 0x176: case 0x336: // dst, dstst, dss
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_datastream( theInstr )) goto decode_success;
goto decode_failure;
@@ -8892,14 +8926,14 @@
case 0x006: case 0x026: // lvsl, lvsr
case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
case 0x067: case 0x167: // lvx, lvxl
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_load( theInstr )) goto decode_success;
goto decode_failure;
/* AV Store */
case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
case 0x0E7: case 0x1E7: // stvx, stvxl
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_store( theInstr )) goto decode_success;
goto decode_failure;
@@ -8918,7 +8952,7 @@
case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_multarith( theInstr )) goto decode_success;
goto decode_failure;
@@ -8926,13 +8960,13 @@
case 0x2A: // vsel
case 0x2B: // vperm
case 0x2C: // vsldoi
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_permute( theInstr )) goto decode_success;
goto decode_failure;
/* AV Floating Point Mult-Add/Sub */
case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_fp_arith( theInstr )) goto decode_success;
goto decode_failure;
@@ -8963,7 +8997,7 @@
case 0x308: case 0x348: // vmulesb, vmulesh
case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
case 0x688: case 0x788: // vsum2sws, vsumsws
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_arith( theInstr )) goto decode_success;
goto decode_failure;
@@ -8974,20 +9008,20 @@
case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
case 0x1C4: case 0x2C4: // vsl, vsr
case 0x40C: case 0x44C: // vslo, vsro
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_shift( theInstr )) goto decode_success;
goto decode_failure;
/* AV Logic */
case 0x404: case 0x444: case 0x484: // vand, vandc, vor
case 0x4C4: case 0x504: // vxor, vnor
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_logic( theInstr )) goto decode_success;
goto decode_failure;
/* AV Processor Control */
case 0x604: case 0x644: // mfvscr, mtvscr
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_procctl( theInstr )) goto decode_success;
goto decode_failure;
@@ -8996,7 +9030,7 @@
case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
case 0x1CA: // vlogefp
case 0x40A: case 0x44A: // vmaxfp, vminfp
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_fp_arith( theInstr )) goto decode_success;
goto decode_failure;
@@ -9005,7 +9039,7 @@
case 0x2CA: // vrfim
case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
case 0x3CA: // vctsxs
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_fp_convert( theInstr )) goto decode_success;
goto decode_failure;
@@ -9014,7 +9048,7 @@
case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_permute( theInstr )) goto decode_success;
goto decode_failure;
@@ -9026,7 +9060,7 @@
case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
case 0x2CE: // vupklsh
case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_pack( theInstr )) goto decode_success;
goto decode_failure;
@@ -9041,14 +9075,14 @@
case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_cmp( theInstr )) goto decode_success;
goto decode_failure;
/* AV Floating Point Compare */
case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
case 0x3C6: // vcmpbfp
- if (!allow_VMX) goto decode_noVMX;
+ if (!allow_V) goto decode_noV;
if (dis_av_fp_cmp( theInstr )) goto decode_success;
goto decode_failure;
@@ -9058,14 +9092,25 @@
break;
default:
- decode_noFP:
- vassert(!allow_FP);
- vex_printf("disInstr(ppc): Floating Point insns disabled for this arch.\n");
goto decode_failure;
- decode_noVMX:
- vassert(!allow_VMX);
- vex_printf("disInstr(ppc): AltiVec insns disabled for this arch.\n");
+ decode_noF:
+ vassert(!allow_F);
+ vex_printf("disInstr(ppc): declined to decode an FP insn.\n");
+ goto decode_failure;
+ decode_noV:
+ vassert(!allow_V);
+ vex_printf("disInstr(ppc): declined to decode an AltiVec insn.\n");
+ goto decode_failure;
+ decode_noFX:
+ vassert(!allow_V);
+ vex_printf("disInstr(ppc): "
+ "declined to decode an GeneralPurpose-Optional insn.\n");
+ goto decode_failure;
+ decode_noGX:
+ vassert(!allow_V);
+ vex_printf("disInstr(ppc): "
+ "declined to decode a Graphics-Optional insn.\n");
goto decode_failure;
decode_failure:
@@ -9125,22 +9170,31 @@
{
IRType ty;
DisResult dres;
- VexSubArch gsa = archinfo->subarch;
+ Bool is32, is64;
+ UInt mask32, mask64;
+ UInt hwcaps_guest = archinfo->hwcaps;
+
+ /* global -- ick */
+ mode64 = False;
/* Figure out whether we're being ppc32 or ppc64 today. */
- switch (gsa) {
- case VexSubArchPPC32_VFI:
- case VexSubArchPPC32_FI:
- case VexSubArchPPC32_I:
- mode64 = False;
- break;
- case VexSubArchPPC64_VFI:
- case VexSubArchPPC64_FI:
- mode64 = True;
- break;
- default:
- vpanic("disInstr_PPC(): illegal subarch");
- }
+ mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V
+ | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX;
+
+ is32 = (hwcaps_guest & ~mask32) > 0;
+
+ mask64 = VEX_HWCAPS_PPC64_V
+ | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX;
+
+ is64 = (hwcaps_guest & ~mask64) > 0;
+
+ if (is32 && !is64)
+ mode64 = False;
+ else if (is64 && !is32)
+ mode64 = True;
+ else
+ vpanic("distInstr_PPC: illegal subarch");
+
ty = mode64 ? Ity_I64 : Ity_I32;
diff --git a/priv/guest-x86/toIR.c b/priv/guest-x86/toIR.c
index 08cc5de..89bd4b1 100644
--- a/priv/guest-x86/toIR.c
+++ b/priv/guest-x86/toIR.c
@@ -7253,7 +7253,7 @@
/* Skip parts of the decoder which don't apply given the stated
guest subarchitecture. */
- if (archinfo->subarch == VexSubArchX86_sse0)
+ if (archinfo->hwcaps == 0/*baseline, no sse at all*/)
goto after_sse_decoders;
/* Otherwise we must be doing sse1 or sse2, so we can at least try
@@ -8274,9 +8274,8 @@
/* Skip parts of the decoder which don't apply given the stated
guest subarchitecture. */
- if (archinfo->subarch == VexSubArchX86_sse0
- || archinfo->subarch == VexSubArchX86_sse1)
- goto after_sse_decoders;
+ if (0 == (archinfo->hwcaps & VEX_HWCAPS_X86_SSE2))
+ goto after_sse_decoders; /* no SSE2 capabilities */
insn = (UChar*)&guest_code[delta];
@@ -10459,10 +10458,12 @@
/* Skip parts of the decoder which don't apply given the stated
guest subarchitecture. */
- if (archinfo->subarch == VexSubArchX86_sse0
- || archinfo->subarch == VexSubArchX86_sse1
- /* || archinfo->subarch == VexSubArchX86_sse2 */)
- goto after_sse_decoders;
+ /* if (0 == (archinfo->hwcaps & VEX_HWCAPS_X86_SSE3)) */
+ /* In fact this is highly bogus; we accept SSE3 insns even on a
+ SSE2-only guest since they turn into IR which can be re-emitted
+ successfully on an SSE2 host. */
+ if (0 == (archinfo->hwcaps & VEX_HWCAPS_X86_SSE2))
+ goto after_sse_decoders; /* no SSE3 capabilities */
insn = (UChar*)&guest_code[delta];
@@ -12148,22 +12149,22 @@
IRDirty* d = NULL;
HChar* fName = NULL;
void* fAddr = NULL;
- switch (archinfo->subarch) {
- case VexSubArchX86_sse0:
- fName = "x86g_dirtyhelper_CPUID_sse0";
- fAddr = &x86g_dirtyhelper_CPUID_sse0;
- break;
- case VexSubArchX86_sse1:
- fName = "x86g_dirtyhelper_CPUID_sse1";
- fAddr = &x86g_dirtyhelper_CPUID_sse1;
- break;
- case VexSubArchX86_sse2:
- fName = "x86g_dirtyhelper_CPUID_sse2";
- fAddr = &x86g_dirtyhelper_CPUID_sse2;
- break;
- default:
- vpanic("disInstr(x86)(cpuid)");
- }
+ if (archinfo->hwcaps & VEX_HWCAPS_X86_SSE2) {
+ fName = "x86g_dirtyhelper_CPUID_sse2";
+ fAddr = &x86g_dirtyhelper_CPUID_sse2;
+ }
+ else
+ if (archinfo->hwcaps & VEX_HWCAPS_X86_SSE1) {
+ fName = "x86g_dirtyhelper_CPUID_sse1";
+ fAddr = &x86g_dirtyhelper_CPUID_sse1;
+ }
+ else
+ if (archinfo->hwcaps == 0/*no SSE*/) {
+ fName = "x86g_dirtyhelper_CPUID_sse0";
+ fAddr = &x86g_dirtyhelper_CPUID_sse0;
+ } else
+ vpanic("disInstr(x86)(cpuid)");
+
vassert(fName); vassert(fAddr);
d = unsafeIRDirty_0_N ( 0/*regparms*/,
fName, fAddr, mkIRExprVec_0() );
diff --git a/priv/host-amd64/isel.c b/priv/host-amd64/isel.c
index 63ea2c1..0fd869c 100644
--- a/priv/host-amd64/isel.c
+++ b/priv/host-amd64/isel.c
@@ -145,7 +145,8 @@
Int vreg_ctr;
- VexSubArch subarch;
+ /* Currently (27 Jan 06) unused */
+ UInt hwcaps;
}
ISelEnv;
@@ -3494,7 +3495,7 @@
vec_fail:
vex_printf("iselVecExpr (amd64, subarch = %s): can't reduce\n",
- LibVEX_ppVexSubArch(env->subarch));
+ LibVEX_ppVexHwCaps(VexArchAMD64, env->hwcaps));
ppIRExpr(e);
vpanic("iselVecExpr_wrk");
}
@@ -3784,13 +3785,13 @@
HInstrArray* iselBB_AMD64 ( IRBB* bb, VexArchInfo* archinfo_host )
{
- Int i, j;
- HReg hreg, hregHI;
- ISelEnv* env;
- VexSubArch subarch_host = archinfo_host->subarch;
+ Int i, j;
+ HReg hreg, hregHI;
+ ISelEnv* env;
+ UInt hwcaps_host = archinfo_host->hwcaps;
/* sanity ... */
- vassert(subarch_host == VexSubArch_NONE);
+ vassert(0 == (hwcaps_host & ~(VEX_HWCAPS_AMD64_SSE3)));
/* Make up an initial environment to use. */
env = LibVEX_Alloc(sizeof(ISelEnv));
@@ -3809,7 +3810,7 @@
env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
/* and finally ... */
- env->subarch = subarch_host;
+ env->hwcaps = hwcaps_host;
/* For each IR temporary, allocate a suitably-kinded virtual
register. */
diff --git a/priv/host-ppc/isel.c b/priv/host-ppc/isel.c
index 7f725a6..4d46477 100644
--- a/priv/host-ppc/isel.c
+++ b/priv/host-ppc/isel.c
@@ -206,7 +206,8 @@
Int vreg_ctr;
- VexSubArch subarch;
+ /* 27 Jan 06: Not currently used, but should be */
+ UInt hwcaps;
Bool mode64;
}
@@ -3350,7 +3351,8 @@
} /* if (e->tag == Iex_Binop) */
vex_printf("iselVecExpr(ppc) (subarch = %s): can't reduce\n",
- LibVEX_ppVexSubArch(env->subarch));
+ LibVEX_ppVexHwCaps(mode64 ? VexArchPPC64 : VexArchPPC32,
+ env->hwcaps));
ppIRExpr(e);
vpanic("iselVecExpr_wrk(ppc)");
}
@@ -3640,26 +3642,31 @@
HInstrArray* iselBB_PPC ( IRBB* bb, VexArchInfo* archinfo_host )
{
- Int i, j;
- HReg hreg, hregHI;
- ISelEnv* env;
- VexSubArch subarch_host = archinfo_host->subarch;
- Bool mode64;
+ Int i, j;
+ HReg hreg, hregHI;
+ ISelEnv* env;
+ UInt hwcaps_host = archinfo_host->hwcaps;
+ Bool mode64 = False;
+ Bool is32, is64;
+ UInt mask32, mask64;
/* Figure out whether we're being ppc32 or ppc64 today. */
- switch (subarch_host) {
- case VexSubArchPPC32_VFI:
- case VexSubArchPPC32_FI:
- case VexSubArchPPC32_I:
+ mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V
+ | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX;
+
+ is32 = (hwcaps_host & ~mask32) > 0;
+
+ mask64 = VEX_HWCAPS_PPC64_V
+ | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX;
+
+ is64 = (hwcaps_host & ~mask64) > 0;
+
+ if (is32 && !is64)
mode64 = False;
- break;
- case VexSubArchPPC64_VFI:
- case VexSubArchPPC64_FI:
+ else if (is64 && !is32)
mode64 = True;
- break;
- default:
+ else
vpanic("iselBB_PPC: illegal subarch");
- }
/* Make up an initial environment to use. */
env = LibVEX_Alloc(sizeof(ISelEnv));
@@ -3681,7 +3688,7 @@
env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
/* and finally ... */
- env->subarch = subarch_host;
+ env->hwcaps = hwcaps_host;
/* For each IR temporary, allocate a suitably-kinded virtual
register. */
diff --git a/priv/host-x86/hdefs.c b/priv/host-x86/hdefs.c
index a53a8c2..0402b69 100644
--- a/priv/host-x86/hdefs.c
+++ b/priv/host-x86/hdefs.c
@@ -703,14 +703,13 @@
i->Xin.Bsfr32.dst = dst;
return i;
}
-X86Instr* X86Instr_MFence ( VexSubArch subarch )
+X86Instr* X86Instr_MFence ( UInt hwcaps )
{
- X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
- i->tag = Xin_MFence;
- i->Xin.MFence.subarch = subarch;
- vassert(subarch == VexSubArchX86_sse0
- || subarch == VexSubArchX86_sse1
- || subarch == VexSubArchX86_sse2);
+ X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
+ i->tag = Xin_MFence;
+ i->Xin.MFence.hwcaps = hwcaps;
+ vassert(0 == (hwcaps & ~(VEX_HWCAPS_X86_SSE1|VEX_HWCAPS_X86_SSE2
+ |VEX_HWCAPS_X86_SSE3)));
return i;
}
@@ -988,7 +987,7 @@
return;
case Xin_MFence:
vex_printf("mfence(%s)",
- LibVEX_ppVexSubArch(i->Xin.MFence.subarch));
+ LibVEX_ppVexHwCaps(VexArchX86,i->Xin.MFence.hwcaps));
return;
case Xin_FpUnary:
vex_printf("g%sD ", showX86FpOp(i->Xin.FpUnary.op));
@@ -2340,26 +2339,28 @@
case Xin_MFence:
/* see comment in hdefs.h re this insn */
if (0) vex_printf("EMIT FENCE\n");
- switch (i->Xin.MFence.subarch) {
- case VexSubArchX86_sse0:
- /* lock addl $0,0(%esp) */
- *p++ = 0xF0; *p++ = 0x83; *p++ = 0x44;
- *p++ = 0x24; *p++ = 0x00; *p++ = 0x00;
- goto done;
- case VexSubArchX86_sse1:
- /* sfence */
- *p++ = 0x0F; *p++ = 0xAE; *p++ = 0xF8;
- /* lock addl $0,0(%esp) */
- *p++ = 0xF0; *p++ = 0x83; *p++ = 0x44;
- *p++ = 0x24; *p++ = 0x00; *p++ = 0x00;
- goto done;
- case VexSubArchX86_sse2:
- /* mfence */
- *p++ = 0x0F; *p++ = 0xAE; *p++ = 0xF0;
- goto done;
- default:
- vpanic("emit_X86Instr:mfence:subarch");
+ if (i->Xin.MFence.hwcaps & (VEX_HWCAPS_X86_SSE3
+ |VEX_HWCAPS_X86_SSE2)) {
+ /* mfence */
+ *p++ = 0x0F; *p++ = 0xAE; *p++ = 0xF0;
+ goto done;
}
+ if (i->Xin.MFence.hwcaps & VEX_HWCAPS_X86_SSE1) {
+ /* sfence */
+ *p++ = 0x0F; *p++ = 0xAE; *p++ = 0xF8;
+ /* lock addl $0,0(%esp) */
+ *p++ = 0xF0; *p++ = 0x83; *p++ = 0x44;
+ *p++ = 0x24; *p++ = 0x00; *p++ = 0x00;
+ goto done;
+ }
+ if (i->Xin.MFence.hwcaps == 0/*baseline, no SSE*/) {
+ /* lock addl $0,0(%esp) */
+ *p++ = 0xF0; *p++ = 0x83; *p++ = 0x44;
+ *p++ = 0x24; *p++ = 0x00; *p++ = 0x00;
+ goto done;
+ }
+ vpanic("emit_X86Instr:mfence:hwcaps");
+ /*NOTREACHED*/
break;
case Xin_Store:
diff --git a/priv/host-x86/hdefs.h b/priv/host-x86/hdefs.h
index b6656ce..5bc8ec6 100644
--- a/priv/host-x86/hdefs.h
+++ b/priv/host-x86/hdefs.h
@@ -491,10 +491,10 @@
much as possible before continuing. On SSE2 we emit a
real "mfence", on SSE1 "sfence ; lock addl $0,0(%esp)" and
on SSE0 "lock addl $0,0(%esp)". This insn therefore
- carries the subarch so the assembler knows what to
+ carries the host's hwcaps so the assembler knows what to
emit. */
struct {
- VexSubArch subarch;
+ UInt hwcaps;
} MFence;
/* X86 Floating point (fake 3-operand, "flat reg file" insns) */
@@ -629,7 +629,7 @@
extern X86Instr* X86Instr_Store ( UChar sz, HReg src, X86AMode* dst );
extern X86Instr* X86Instr_Set32 ( X86CondCode cond, HReg dst );
extern X86Instr* X86Instr_Bsfr32 ( Bool isFwds, HReg src, HReg dst );
-extern X86Instr* X86Instr_MFence ( VexSubArch );
+extern X86Instr* X86Instr_MFence ( UInt hwcaps );
extern X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst );
extern X86Instr* X86Instr_FpBinary ( X86FpOp op, HReg srcL, HReg srcR, HReg dst );
diff --git a/priv/host-x86/isel.c b/priv/host-x86/isel.c
index 50fb29a..8174dfd 100644
--- a/priv/host-x86/isel.c
+++ b/priv/host-x86/isel.c
@@ -158,7 +158,7 @@
Int vreg_ctr;
- VexSubArch subarch;
+ UInt hwcaps;
}
ISelEnv;
@@ -2773,20 +2773,18 @@
static HReg iselVecExpr_wrk ( ISelEnv* env, IRExpr* e )
{
-# define REQUIRE_SSE1 \
- do { if (env->subarch == VexSubArchX86_sse0) \
- goto vec_fail; \
+# define REQUIRE_SSE1 \
+ do { if (env->hwcaps == 0/*baseline, no sse*/) \
+ goto vec_fail; \
} while (0)
-# define REQUIRE_SSE2 \
- do { if (env->subarch == VexSubArchX86_sse0 \
- || env->subarch == VexSubArchX86_sse1) \
- goto vec_fail; \
+# define REQUIRE_SSE2 \
+ do { if (0 == (env->hwcaps & VEX_HWCAPS_X86_SSE2)) \
+ goto vec_fail; \
} while (0)
-# define SSE2_OR_ABOVE \
- (env->subarch != VexSubArchX86_sse0 \
- && env->subarch != VexSubArchX86_sse1)
+# define SSE2_OR_ABOVE \
+ (env->hwcaps & VEX_HWCAPS_X86_SSE2)
MatchInfo mi;
Bool arg1isEReg = False;
@@ -3271,8 +3269,8 @@
}
vec_fail:
- vex_printf("iselVecExpr (subarch = %s): can't reduce\n",
- LibVEX_ppVexSubArch(env->subarch));
+ vex_printf("iselVecExpr (hwcaps = %s): can't reduce\n",
+ LibVEX_ppVexHwCaps(VexArchX86,env->hwcaps));
ppIRExpr(e);
vpanic("iselVecExpr_wrk");
@@ -3522,7 +3520,7 @@
/* --------- MEM FENCE --------- */
case Ist_MFence:
- addInstr(env, X86Instr_MFence(env->subarch));
+ addInstr(env, X86Instr_MFence(env->hwcaps));
return;
/* --------- INSTR MARK --------- */
@@ -3582,15 +3580,15 @@
HInstrArray* iselBB_X86 ( IRBB* bb, VexArchInfo* archinfo_host )
{
- Int i, j;
- HReg hreg, hregHI;
- ISelEnv* env;
- VexSubArch subarch_host = archinfo_host->subarch;
+ Int i, j;
+ HReg hreg, hregHI;
+ ISelEnv* env;
+ UInt hwcaps_host = archinfo_host->hwcaps;
/* sanity ... */
- vassert(subarch_host == VexSubArchX86_sse0
- || subarch_host == VexSubArchX86_sse1
- || subarch_host == VexSubArchX86_sse2);
+ vassert(0 == (hwcaps_host & ~(VEX_HWCAPS_X86_SSE1
+ |VEX_HWCAPS_X86_SSE2
+ |VEX_HWCAPS_X86_SSE3)));
/* Make up an initial environment to use. */
env = LibVEX_Alloc(sizeof(ISelEnv));
@@ -3609,7 +3607,7 @@
env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
/* and finally ... */
- env->subarch = subarch_host;
+ env->hwcaps = hwcaps_host;
/* For each IR temporary, allocate a suitably-kinded virtual
register. */
diff --git a/priv/main/vex_main.c b/priv/main/vex_main.c
index b232fbf..a93a17a 100644
--- a/priv/main/vex_main.c
+++ b/priv/main/vex_main.c
@@ -70,6 +70,12 @@
/* This file contains the top level interface to the library. */
+/* --------- fwds ... --------- */
+
+static Bool are_valid_hwcaps ( VexArch arch, UInt hwcaps );
+static HChar* show_hwcaps ( VexArch arch, UInt hwcaps );
+
+
/* --------- Initialise the library. --------- */
/* Exported to library client. */
@@ -253,9 +259,7 @@
emit = (Int(*)(UChar*,Int,HInstr*,Bool,void*)) emit_X86Instr;
host_is_bigendian = False;
host_word_type = Ity_I32;
- vassert(vta->archinfo_host.subarch == VexSubArchX86_sse0
- || vta->archinfo_host.subarch == VexSubArchX86_sse1
- || vta->archinfo_host.subarch == VexSubArchX86_sse2);
+ vassert(are_valid_hwcaps(VexArchX86, vta->archinfo_host.hwcaps));
vassert(vta->dispatch != NULL); /* jump-to-dispatcher scheme */
break;
@@ -274,7 +278,7 @@
emit = (Int(*)(UChar*,Int,HInstr*,Bool,void*)) emit_AMD64Instr;
host_is_bigendian = False;
host_word_type = Ity_I64;
- vassert(vta->archinfo_host.subarch == VexSubArch_NONE);
+ vassert(are_valid_hwcaps(VexArchAMD64, vta->archinfo_host.hwcaps));
vassert(vta->dispatch != NULL); /* jump-to-dispatcher scheme */
break;
@@ -293,9 +297,7 @@
emit = (Int(*)(UChar*,Int,HInstr*,Bool,void*)) emit_PPCInstr;
host_is_bigendian = True;
host_word_type = Ity_I32;
- vassert(vta->archinfo_guest.subarch == VexSubArchPPC32_I
- || vta->archinfo_guest.subarch == VexSubArchPPC32_FI
- || vta->archinfo_guest.subarch == VexSubArchPPC32_VFI);
+ vassert(are_valid_hwcaps(VexArchPPC32, vta->archinfo_host.hwcaps));
vassert(vta->dispatch == NULL); /* return-to-dispatcher scheme */
break;
@@ -314,8 +316,7 @@
emit = (Int(*)(UChar*,Int,HInstr*,Bool,void*)) emit_PPCInstr;
host_is_bigendian = True;
host_word_type = Ity_I64;
- vassert(vta->archinfo_guest.subarch == VexSubArchPPC64_FI
- || vta->archinfo_guest.subarch == VexSubArchPPC64_VFI);
+ vassert(are_valid_hwcaps(VexArchPPC64, vta->archinfo_host.hwcaps));
vassert(vta->dispatch == NULL); /* return-to-dispatcher scheme */
break;
@@ -335,9 +336,7 @@
guest_layout = &x86guest_layout;
offB_TISTART = offsetof(VexGuestX86State,guest_TISTART);
offB_TILEN = offsetof(VexGuestX86State,guest_TILEN);
- vassert(vta->archinfo_guest.subarch == VexSubArchX86_sse0
- || vta->archinfo_guest.subarch == VexSubArchX86_sse1
- || vta->archinfo_guest.subarch == VexSubArchX86_sse2);
+ vassert(are_valid_hwcaps(VexArchX86, vta->archinfo_guest.hwcaps));
vassert(0 == sizeof(VexGuestX86State) % 8);
vassert(sizeof( ((VexGuestX86State*)0)->guest_TISTART) == 4);
vassert(sizeof( ((VexGuestX86State*)0)->guest_TILEN ) == 4);
@@ -353,7 +352,7 @@
guest_layout = &amd64guest_layout;
offB_TISTART = offsetof(VexGuestAMD64State,guest_TISTART);
offB_TILEN = offsetof(VexGuestAMD64State,guest_TILEN);
- vassert(vta->archinfo_guest.subarch == VexSubArch_NONE);
+ vassert(are_valid_hwcaps(VexArchAMD64, vta->archinfo_guest.hwcaps));
vassert(0 == sizeof(VexGuestAMD64State) % 8);
vassert(sizeof( ((VexGuestAMD64State*)0)->guest_TISTART ) == 8);
vassert(sizeof( ((VexGuestAMD64State*)0)->guest_TILEN ) == 8);
@@ -369,7 +368,7 @@
guest_layout = &armGuest_layout;
offB_TISTART = 0; /* hack ... arm has bitrot */
offB_TILEN = 0; /* hack ... arm has bitrot */
- vassert(vta->archinfo_guest.subarch == VexSubArchARM_v4);
+ vassert(are_valid_hwcaps(VexArchARM, vta->archinfo_guest.hwcaps));
break;
case VexArchPPC32:
@@ -381,9 +380,7 @@
guest_layout = &ppc32Guest_layout;
offB_TISTART = offsetof(VexGuestPPC32State,guest_TISTART);
offB_TILEN = offsetof(VexGuestPPC32State,guest_TILEN);
- vassert(vta->archinfo_guest.subarch == VexSubArchPPC32_I
- || vta->archinfo_guest.subarch == VexSubArchPPC32_FI
- || vta->archinfo_guest.subarch == VexSubArchPPC32_VFI);
+ vassert(are_valid_hwcaps(VexArchPPC32, vta->archinfo_guest.hwcaps));
vassert(0 == sizeof(VexGuestPPC32State) % 8);
vassert(sizeof( ((VexGuestPPC32State*)0)->guest_TISTART ) == 4);
vassert(sizeof( ((VexGuestPPC32State*)0)->guest_TILEN ) == 4);
@@ -399,8 +396,7 @@
guest_layout = &ppc64Guest_layout;
offB_TISTART = offsetof(VexGuestPPC64State,guest_TISTART);
offB_TILEN = offsetof(VexGuestPPC64State,guest_TILEN);
- vassert(vta->archinfo_guest.subarch == VexSubArchPPC64_FI
- || vta->archinfo_guest.subarch == VexSubArchPPC64_VFI);
+ vassert(are_valid_hwcaps(VexArchPPC64, vta->archinfo_guest.hwcaps));
vassert(0 == sizeof(VexGuestPPC64State) % 16);
vassert(sizeof( ((VexGuestPPC64State*)0)->guest_TISTART ) == 8);
vassert(sizeof( ((VexGuestPPC64State*)0)->guest_TILEN ) == 8);
@@ -416,7 +412,7 @@
/* doesn't necessarily have to be true, but if it isn't it means
we are simulating one flavour of an architecture a different
flavour of the same architecture, which is pretty strange. */
- vassert(vta->archinfo_guest.subarch == vta->archinfo_host.subarch);
+ vassert(vta->archinfo_guest.hwcaps == vta->archinfo_host.hwcaps);
}
vexAllocSanityCheck();
@@ -675,7 +671,7 @@
}
}
-/* --------- Arch/Subarch stuff. --------- */
+/* ------------------ Arch/HwCaps stuff. ------------------ */
const HChar* LibVEX_ppVexArch ( VexArch arch )
{
@@ -690,32 +686,117 @@
}
}
-const HChar* LibVEX_ppVexSubArch ( VexSubArch subarch )
+const HChar* LibVEX_ppVexHwCaps ( VexArch arch, UInt hwcaps )
{
- switch (subarch) {
- case VexSubArch_INVALID: return "INVALID";
- case VexSubArch_NONE: return "NONE";
- case VexSubArchX86_sse0: return "x86-sse0";
- case VexSubArchX86_sse1: return "x86-sse1";
- case VexSubArchX86_sse2: return "x86-sse2";
- case VexSubArchARM_v4: return "arm-v4";
- case VexSubArchPPC32_I: return "ppc32-int-only";
- case VexSubArchPPC32_FI: return "ppc32-int-and-fp";
- case VexSubArchPPC32_VFI: return "ppc32-int-fp-and-AV";
- case VexSubArchPPC64_FI: return "ppc64-int-and-fp";
- case VexSubArchPPC64_VFI: return "ppc64-int-fp-and-AV";
- default: return "VexSubArch???";
- }
+ HChar* str = show_hwcaps(arch,hwcaps);
+ return str ? str : "INVALID";
}
+
+
/* Write default settings info *vai. */
void LibVEX_default_VexArchInfo ( /*OUT*/VexArchInfo* vai )
{
- vai->subarch = VexSubArch_INVALID;
+ vai->hwcaps = 0;
vai->ppc_cache_line_szB = 0;
}
+/* Return a string showing the hwcaps in a nice way. The string will
+ be NULL for invalid combinations of flags, so these functions also
+ serve as a way to validate hwcaps values. */
+
+static HChar* show_hwcaps_x86 ( UInt hwcaps )
+{
+ /* Monotonic, SSE3 > SSE2 > SSE1 > baseline. */
+ if (hwcaps == 0)
+ return "x86-sse0";
+ if (hwcaps == VEX_HWCAPS_X86_SSE1)
+ return "x86-sse1";
+ if (hwcaps == (VEX_HWCAPS_X86_SSE1 | VEX_HWCAPS_X86_SSE2))
+ return "x86-sse1-sse2";
+ if (hwcaps == (VEX_HWCAPS_X86_SSE1
+ | VEX_HWCAPS_X86_SSE2 | VEX_HWCAPS_X86_SSE3))
+ return "x86-sse1-sse2-sse3";
+
+ return False;
+}
+
+static HChar* show_hwcaps_amd64 ( UInt hwcaps )
+{
+ /* Monotonic, SSE3 > baseline. */
+ if (hwcaps == 0)
+ return "amd64-sse2";
+ if (hwcaps == VEX_HWCAPS_AMD64_SSE3)
+ return "amd64-sse3";
+ return False;
+}
+
+static HChar* show_hwcaps_ppc32 ( UInt hwcaps )
+{
+ /* Monotonic with complications. Basically V > F > baseline,
+ but once you have F then you can have FX or GX too. */
+ const UInt F = VEX_HWCAPS_PPC32_F;
+ const UInt V = VEX_HWCAPS_PPC32_V;
+ const UInt FX = VEX_HWCAPS_PPC32_FX;
+ const UInt GX = VEX_HWCAPS_PPC32_GX;
+ UInt c = hwcaps;
+ if (c == 0) return "ppc32-int";
+ if (c == F) return "ppc32-int-flt";
+ if (c == (F|FX)) return "ppc32-int-flt-FX";
+ if (c == (F|GX)) return "ppc32-int-flt-GX";
+ if (c == (F|FX|GX)) return "ppc32-int-flt-FX-GX";
+ if (c == (F|V)) return "ppc32-int-flt-vmx";
+ if (c == (F|V|FX)) return "ppc32-int-flt-vmx-FX";
+ if (c == (F|V|GX)) return "ppc32-int-flt-vmx-GX";
+ if (c == (F|V|FX|GX)) return "ppc32-int-flt-vmx-FX-GX";
+ return NULL;
+}
+
+static HChar* show_hwcaps_ppc64 ( UInt hwcaps )
+{
+ /* Monotonic with complications. Basically V > baseline(==F),
+ but once you have F then you can have FX or GX too. */
+ const UInt V = VEX_HWCAPS_PPC32_V;
+ const UInt FX = VEX_HWCAPS_PPC32_FX;
+ const UInt GX = VEX_HWCAPS_PPC32_GX;
+ UInt c = hwcaps;
+ if (c == 0) return "ppc64-int-flt";
+ if (c == FX) return "ppc64-int-flt-FX";
+ if (c == GX) return "ppc64-int-flt-GX";
+ if (c == (FX|GX)) return "ppc64-int-flt-FX-GX";
+ if (c == V) return "ppc64-int-flt-vmx";
+ if (c == (V|FX)) return "ppc64-int-flt-vmx-FX";
+ if (c == (V|GX)) return "ppc64-int-flt-vmx-GX";
+ if (c == (V|FX|GX)) return "ppc64-int-flt-vmx-FX-GX";
+ return NULL;
+}
+
+static HChar* show_hwcaps_arm ( UInt hwcaps )
+{
+ if (hwcaps == 0) return "arm-baseline";
+ return NULL;
+}
+
+/* ---- */
+static HChar* show_hwcaps ( VexArch arch, UInt hwcaps )
+{
+ switch (arch) {
+ case VexArchX86: return show_hwcaps_x86(hwcaps);
+ case VexArchAMD64: return show_hwcaps_amd64(hwcaps);
+ case VexArchPPC32: return show_hwcaps_ppc32(hwcaps);
+ case VexArchPPC64: return show_hwcaps_ppc64(hwcaps);
+ case VexArchARM: return show_hwcaps_arm(hwcaps);
+ default: return NULL;
+ }
+}
+
+static Bool are_valid_hwcaps ( VexArch arch, UInt hwcaps )
+{
+ return show_hwcaps(arch,hwcaps) != NULL;
+}
+
+
/*---------------------------------------------------------------*/
/*--- end main/vex_main.c ---*/
/*---------------------------------------------------------------*/