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