+ return true;
+}
+
+/**
+ * returns true if it is assured, that the upper bits of a node are "clean"
+ * which means for a 16 or 8 bit value, that the upper bits in the register
+ * are 0 for unsigned and a copy of the last significant bit for signed
+ * numbers.
+ */
+static bool upper_bits_clean(ir_node *transformed_node, ir_mode *mode)
+{
+ assert(ia32_mode_needs_gp_reg(mode));
+ if (get_mode_size_bits(mode) >= 32)
+ return true;
+
+ if (is_Proj(transformed_node))
+ return upper_bits_clean(get_Proj_pred(transformed_node), mode);
+
+ switch (get_ia32_irn_opcode(transformed_node)) {
+ case iro_ia32_Conv_I2I:
+ case iro_ia32_Conv_I2I8Bit: {
+ ir_mode *smaller_mode = get_ia32_ls_mode(transformed_node);
+ if (mode_is_signed(smaller_mode) != mode_is_signed(mode))
+ return false;
+ if (get_mode_size_bits(smaller_mode) > get_mode_size_bits(mode))
+ return false;
+
+ return true;
+ }
+
+ case iro_ia32_Shr:
+ if (mode_is_signed(mode)) {
+ return false; /* TODO handle signed modes */
+ } else {
+ ir_node *right = get_irn_n(transformed_node, n_ia32_Shr_count);
+ if (is_ia32_Immediate(right) || is_ia32_Const(right)) {
+ const ia32_immediate_attr_t *attr
+ = get_ia32_immediate_attr_const(right);
+ if (attr->symconst == 0 &&
+ (unsigned)attr->offset >= 32 - get_mode_size_bits(mode)) {
+ return true;
+ }
+ }
+ return upper_bits_clean(get_irn_n(transformed_node, n_ia32_Shr_val), mode);
+ }
+
+ case iro_ia32_And:
+ if (!mode_is_signed(mode)) {
+ return
+ upper_bits_clean(get_irn_n(transformed_node, n_ia32_And_right), mode) ||
+ upper_bits_clean(get_irn_n(transformed_node, n_ia32_And_left), mode);
+ }
+ /* TODO if one is known to be zero extended, then || is sufficient */
+ /* FALLTHROUGH */
+ case iro_ia32_Or:
+ case iro_ia32_Xor:
+ return
+ upper_bits_clean(get_irn_n(transformed_node, n_ia32_binary_right), mode) &&
+ upper_bits_clean(get_irn_n(transformed_node, n_ia32_binary_left), mode);
+
+ case iro_ia32_Const:
+ case iro_ia32_Immediate: {
+ const ia32_immediate_attr_t *attr =
+ get_ia32_immediate_attr_const(transformed_node);
+ if (mode_is_signed(mode)) {
+ long shifted = attr->offset >> (get_mode_size_bits(mode) - 1);
+ return shifted == 0 || shifted == -1;
+ } else {
+ unsigned long shifted = (unsigned long)attr->offset;
+ shifted >>= get_mode_size_bits(mode);
+ return shifted == 0;
+ }
+ }
+
+ default:
+ return false;
+ }