| Index: pkg/compiler/lib/src/js_backend/codegen/codegen.dart | 
| diff --git a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart | 
| index 14563e1b51a10219b58dd7c10d7990921f0fc4f7..92852b7e7c04db1ca1b8857971a57c5320b79d5f 100644 | 
| --- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart | 
| +++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart | 
| @@ -858,12 +858,18 @@ class CodeGenerator extends tree_ir.StatementVisitor | 
| return new js.Binary('-', args[0], args[1]); | 
| case BuiltinOperator.NumMultiply: | 
| return new js.Binary('*', args[0], args[1]); | 
| +      case BuiltinOperator.NumDivide: | 
| +        return new js.Binary('/', args[0], args[1]); | 
| +      case BuiltinOperator.NumRemainder: | 
| +        return new js.Binary('%', args[0], args[1]); | 
| +      case BuiltinOperator.NumTruncatingDivideToSigned32: | 
| +        return js.js('(# / #) | 0', args); | 
| case BuiltinOperator.NumAnd: | 
| -        return js.js('(# & #) >>> 0', args); | 
| +        return normalizeBitOp(js.js('# & #', args), node); | 
| case BuiltinOperator.NumOr: | 
| -        return js.js('(# | #) >>> 0', args); | 
| +        return normalizeBitOp(js.js('# | #', args), node); | 
| case BuiltinOperator.NumXor: | 
| -        return js.js('(# ^ #) >>> 0', args); | 
| +        return normalizeBitOp(js.js('# ^ #', args), node); | 
| case BuiltinOperator.NumLt: | 
| return new js.Binary('<', args[0], args[1]); | 
| case BuiltinOperator.NumLe: | 
| @@ -873,7 +879,10 @@ class CodeGenerator extends tree_ir.StatementVisitor | 
| case BuiltinOperator.NumGe: | 
| return new js.Binary('>=', args[0], args[1]); | 
| case BuiltinOperator.NumShl: | 
| -        return js.js('(# << #) >>> 0', args); | 
| +        return normalizeBitOp(js.js('# << #', args), node); | 
| +      case BuiltinOperator.NumShr: | 
| +        // No normalization required since output is always uint32. | 
| +        return js.js('# >>> #', args); | 
| case BuiltinOperator.StringConcatenate: | 
| if (args.isEmpty) return js.string(''); | 
| return args.reduce((e1,e2) => new js.Binary('+', e1, e2)); | 
| @@ -911,6 +920,74 @@ class CodeGenerator extends tree_ir.StatementVisitor | 
| } | 
| } | 
|  | 
| +  /// Add a uint32 normalization `op >>> 0` to [op] if it is not in 31-bit | 
| +  /// range. | 
| +  js.Expression normalizeBitOp(js.Expression op, | 
| +                               tree_ir.ApplyBuiltinOperator node) { | 
| +    const MAX_UINT31 = 0x7fffffff; | 
| +    const MAX_UINT32 = 0xffffffff; | 
| + | 
| +    int constantValue(tree_ir.Expression e) { | 
| +      if (e is tree_ir.Constant) { | 
| +        ConstantValue value = e.value; | 
| +        if (!value.isInt) return null; | 
| +        IntConstantValue intConstant = value; | 
| +        if (intConstant.primitiveValue < 0) return null; | 
| +        if (intConstant.primitiveValue > MAX_UINT32) return null; | 
| +        return intConstant.primitiveValue; | 
| +      } | 
| +      return null; | 
| +    } | 
| + | 
| +    /// Returns a value of the form 0b0001xxxx to represent the highest bit set | 
| +    /// in the result.  This represents the range [0, 0b00011111], up to 32 | 
| +    /// bits.  `null` represents a result possibly outside the uint32 range. | 
| +    int maxBitOf(tree_ir.Expression e) { | 
| +      if (e is tree_ir.Constant) { | 
| +        return constantValue(e); | 
| +      } | 
| +      if (e is tree_ir.ApplyBuiltinOperator) { | 
| +        if (e.operator == BuiltinOperator.NumAnd) { | 
| +          int left = maxBitOf(e.arguments[0]); | 
| +          int right = maxBitOf(e.arguments[1]); | 
| +          if (left == null && right == null) return MAX_UINT32; | 
| +          if (left == null) return right; | 
| +          if (right == null) return left; | 
| +          return (left < right) ? left : right; | 
| +        } | 
| +        if (e.operator == BuiltinOperator.NumOr || | 
| +            e.operator == BuiltinOperator.NumXor) { | 
| +          int left = maxBitOf(e.arguments[0]); | 
| +          int right = maxBitOf(e.arguments[1]); | 
| +          if (left == null || right == null) return MAX_UINT32; | 
| +          return left | right; | 
| +        } | 
| +        if (e.operator == BuiltinOperator.NumShr) { | 
| +          int right = constantValue(e.arguments[1]); | 
| +          // NumShr is JavaScript '>>>' so always generates a uint32 result. | 
| +          if (right == null || right <= 0 || right > 31) return MAX_UINT32; | 
| +          int left = maxBitOf(e.arguments[0]); | 
| +          if (left == null) return MAX_UINT32; | 
| +          return left >> right; | 
| +        } | 
| +        if (e.operator == BuiltinOperator.NumShl) { | 
| +          int right = constantValue(e.arguments[1]); | 
| +          if (right == null || right <= 0 || right > 31) return MAX_UINT32; | 
| +          int left = maxBitOf(e.arguments[0]); | 
| +          if (left == null) return MAX_UINT32; | 
| +          if (left.bitLength + right > 31) return MAX_UINT32; | 
| +          return left << right; | 
| +        } | 
| +      } | 
| +      return null; | 
| +    } | 
| + | 
| +    int maxBit = maxBitOf(node); | 
| +    if (maxBit != null && maxBit <= MAX_UINT31) return op; | 
| +    return js.js('# >>> 0', [op]); | 
| +  } | 
| + | 
| + | 
| /// The JS name of a built-in method. | 
| static final Map<BuiltinMethod, String> builtinMethodName = | 
| const <BuiltinMethod, String>{ | 
|  |