< prev index next >
src/cpu/sparc/vm/assembler_sparc.hpp
Print this page
@@ -463,10 +463,40 @@
}
static bool is_cxb(int x) {
assert(is_cbcond(x), "wrong instruction");
return (x & (1 << 21)) != 0;
}
+ static bool is_branch(int x) {
+ if (inv_op(x) != Assembler::branch_op) return false;
+
+ bool is_bpr = inv_op2(x) == Assembler::bpr_op2;
+ bool is_bp = inv_op2(x) == Assembler::bp_op2;
+ bool is_br = inv_op2(x) == Assembler::br_op2;
+ bool is_fp = inv_op2(x) == Assembler::fb_op2;
+ bool is_fbp = inv_op2(x) == Assembler::fbp_op2;
+
+ return is_bpr || is_bp || is_br || is_fp || is_fbp;
+ }
+ static bool is_call(int x) {
+ return inv_op(x) == Assembler::call_op;
+ }
+ static bool is_jump(int x) {
+ if (inv_op(x) != Assembler::arith_op) return false;
+
+ bool is_jmpl = inv_op3(x) == Assembler::jmpl_op3;
+ bool is_rett = inv_op3(x) == Assembler::rett_op3;
+
+ return is_jmpl || is_rett;
+ }
+ static bool is_rdpc(int x) {
+ return (inv_op(x) == Assembler::arith_op && inv_op3(x) == Assembler::rdreg_op3 &&
+ inv_u_field(x, 18, 14) == 5);
+ }
+ static bool is_cti(int x) {
+ return is_branch(x) || is_call(x) || is_jump(x); // Ignoring done/retry
+ }
+
static int cond_cbcond(int x) { return u_field((((x & 8) << 1) + 8 + (x & 7)), 29, 25); }
static int inv_cond_cbcond(int x) {
assert(is_cbcond(x), "wrong instruction");
return inv_u_field(x, 27, 25) | (inv_u_field(x, 29, 29) << 3);
}
@@ -609,97 +639,134 @@
// instruction deprecated in v9
static void v9_dep() { } // do nothing for now
protected:
- // Simple delay-slot scheme:
- // In order to check the programmer, the assembler keeps track of delay slots.
- // It forbids CTIs in delay slots (conservative, but should be OK).
- // Also, when putting an instruction into a delay slot, you must say
- // asm->delayed()->add(...), in order to check that you don't omit
- // delay-slot instructions.
- // To implement this, we use a simple FSA
-
#ifdef ASSERT
- #define CHECK_DELAY
+#define VALIDATE_PIPELINE
#endif
-#ifdef CHECK_DELAY
- enum Delay_state { no_delay, at_delay_slot, filling_delay_slot } delay_state;
+
+#ifdef VALIDATE_PIPELINE
+ // A simple delay-slot scheme:
+ // In order to check the programmer, the assembler keeps track of delay-slots.
+ // It forbids CTIs in delay-slots (conservative, but should be OK). Also, when
+ // emitting an instruction into a delay-slot, you must do so using delayed(),
+ // e.g. asm->delayed()->add(...), in order to check that you do not omit the
+ // delay-slot instruction. To implement this, we use a simple FSA.
+ enum { NoDelay, AtDelay, FillDelay } _delay_state;
+
+ // A simple hazard scheme:
+ // In order to avoid pipeline stalls, due to single cycle pipeline hazards, we
+ // adopt a simplistic state tracking mechanism that will enforce an additional
+ // 'nop' instruction to be inserted prior to emitting an instruction that can
+ // expose a given hazard (currently, PC-related hazards only).
+ enum { NoHazard, PcHazard } _hazard_state;
#endif
public:
- // Tells assembler next instruction must NOT be in delay slot.
- // Use at start of multinstruction macros.
+ // Tell the assembler that the next instruction must NOT be in delay-slot.
+ // Use at start of multi-instruction macros.
void assert_not_delayed() {
- // This is a separate overloading to avoid creation of string constants
- // in non-asserted code--with some compilers this pollutes the object code.
-#ifdef CHECK_DELAY
- assert_not_delayed("next instruction should not be a delay slot");
+ // This is a separate entry to avoid the creation of string constants in
+ // non-asserted code, with some compilers this pollutes the object code.
+#ifdef VALIDATE_PIPELINE
+ assert_no_delay("Next instruction should not be in a delay-slot.");
#endif
}
- void assert_not_delayed(const char* msg) {
-#ifdef CHECK_DELAY
- assert(delay_state == no_delay, msg);
+
+ protected:
+ void assert_no_delay(const char* msg) {
+#ifdef VALIDATE_PIPELINE
+ assert(_delay_state == NoDelay, msg);
#endif
}
+ void assert_no_hazard() {
+#ifdef VALIDATE_PIPELINE
+ assert(_hazard_state == NoHazard, "Unsolicited pipeline hazard.");
+#endif
+ }
+
+ private:
+ inline int32_t prev_insn() {
+ assert(offset() > 0, "Interface violation.");
+ int32_t* addr = (int32_t*)pc() - 1;
+ return *addr;
+ }
+
+#ifdef VALIDATE_PIPELINE
+ void validate_no_pipeline_hazards();
+#endif
+
protected:
- // Insert a nop if the previous is cbcond
- inline void insert_nop_after_cbcond();
+ // Avoid possible pipeline stall by inserting an additional 'nop' instruction,
+ // if the previous instruction is a 'cbcond' or a 'rdpc'.
+ inline void avoid_pipeline_stall();
- // Delay slot helpers
- // cti is called when emitting control-transfer instruction,
- // BEFORE doing the emitting.
- // Only effective when assertion-checking is enabled.
+ // A call to cti() is made before emitting a control-transfer instruction (CTI)
+ // in order to assert a CTI is not emitted right after a 'cbcond', nor in the
+ // delay-slot of another CTI. Only effective when assertions are enabled.
void cti() {
- // A cbcond instruction immediately followed by a CTI
- // instruction introduces pipeline stalls, we need to avoid that.
- no_cbcond_before();
-#ifdef CHECK_DELAY
- assert_not_delayed("cti should not be in delay slot");
+ // A 'cbcond' or 'rdpc' instruction immediately followed by a CTI introduces
+ // a pipeline stall, which we make sure to prohibit.
+ assert_no_cbcond_before();
+ assert_no_rdpc_before();
+#ifdef VALIDATE_PIPELINE
+ assert_no_hazard();
+ assert_no_delay("CTI in delay-slot.");
#endif
}
- // called when emitting cti with a delay slot, AFTER emitting
- void has_delay_slot() {
-#ifdef CHECK_DELAY
- assert_not_delayed("just checking");
- delay_state = at_delay_slot;
+ // Called when emitting CTI with a delay-slot, AFTER emitting.
+ inline void induce_delay_slot() {
+#ifdef VALIDATE_PIPELINE
+ assert_no_delay("Already in delay-slot.");
+ _delay_state = AtDelay;
#endif
}
- // cbcond instruction should not be generated one after an other
- bool cbcond_before() {
- if (offset() == 0) return false; // it is first instruction
- int x = *(int*)(intptr_t(pc()) - 4); // previous instruction
- return is_cbcond(x);
+ inline void induce_pc_hazard() {
+#ifdef VALIDATE_PIPELINE
+ assert_no_hazard();
+ _hazard_state = PcHazard;
+#endif
}
- void no_cbcond_before() {
- assert(offset() == 0 || !cbcond_before(), "cbcond should not follow an other cbcond");
+ bool is_cbcond_before() { return offset() > 0 ? is_cbcond(prev_insn()) : false; }
+
+ bool is_rdpc_before() { return offset() > 0 ? is_rdpc(prev_insn()) : false; }
+
+ void assert_no_cbcond_before() {
+ assert(offset() == 0 || !is_cbcond_before(), "CBCOND should not be followed by CTI.");
}
-public:
+ void assert_no_rdpc_before() {
+ assert(offset() == 0 || !is_rdpc_before(), "RDPC should not be followed by CTI.");
+ }
+
+ public:
+
bool use_cbcond(Label &L) {
- if (!UseCBCond || cbcond_before()) return false;
+ if (!UseCBCond || is_cbcond_before()) return false;
intptr_t x = intptr_t(target_distance(L)) - intptr_t(pc());
assert((x & 3) == 0, "not word aligned");
return is_simm12(x);
}
// Tells assembler you know that next instruction is delayed
Assembler* delayed() {
-#ifdef CHECK_DELAY
- assert(delay_state == at_delay_slot, "delayed instruction is not in delay slot");
- delay_state = filling_delay_slot;
+#ifdef VALIDATE_PIPELINE
+ assert(_delay_state == AtDelay, "Delayed instruction not in delay-slot.");
+ _delay_state = FillDelay;
#endif
return this;
}
void flush() {
-#ifdef CHECK_DELAY
- assert(delay_state == no_delay, "ending code with a delay slot");
+#ifdef VALIDATE_PIPELINE
+ assert(_delay_state == NoDelay, "Ending code with a delay-slot.");
+ validate_no_pipeline_hazards();
#endif
AbstractAssembler::flush();
}
inline void emit_int32(int); // shadows AbstractAssembler::emit_int32
@@ -1213,12 +1280,13 @@
inline void crc32c(FloatRegister s1, FloatRegister s2, FloatRegister d);
// Creation
Assembler(CodeBuffer* code) : AbstractAssembler(code) {
-#ifdef CHECK_DELAY
- delay_state = no_delay;
+#ifdef VALIDATE_PIPELINE
+ _delay_state = NoDelay;
+ _hazard_state = NoHazard;
#endif
}
};
#endif // CPU_SPARC_VM_ASSEMBLER_SPARC_HPP
< prev index next >