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>{ |