Skip to content

Commit 8c745c6

Browse files
committed
Fix modulo and bitwise operators.
Dart `%` is modulo, JS `%` is remainder, so call the runtime version. Use dart2js interpretation of bit operations and shifts which always produce a 32-bit unsigned result. Optimization of the bit operations to follow. BUG= dart-archive/dev_compiler#518 [email protected] Review URL: https://codereview.chromium.org/1924413002 .
1 parent 9f764bf commit 8c745c6

File tree

6 files changed

+350
-302
lines changed

6 files changed

+350
-302
lines changed

pkg/dev_compiler/lib/runtime/dart_sdk.js

Lines changed: 282 additions & 282 deletions
Large diffs are not rendered by default.

pkg/dev_compiler/lib/src/compiler/code_generator.dart

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,21 +2810,61 @@ class CodeGenerator extends GeneralizingAstVisitor
28102810
// these values are assumed to be non-null (determined by the checker)
28112811
// TODO(jmesserly): it would be nice to just inline the method from core,
28122812
// instead of special cases here.
2813-
if (op.type == TokenType.TILDE_SLASH) {
2814-
// `a ~/ b` is equivalent to `(a / b).truncate()`
2815-
var div = AstBuilder.binaryExpression(left, '/', right)
2816-
..staticType = node.staticType;
2817-
return _emitSend(div, 'truncate', []);
2818-
} else {
2819-
// TODO(vsm): When do Dart ops not map to JS?
2820-
code = '# $op #';
2813+
JS.Expression binary(String code) {
2814+
return js.call(code, [notNull(left), notNull(right)]);
2815+
}
2816+
2817+
JS.Expression bitwise(String code) {
2818+
return _coerceBitOperationResultToUnsigned(node, binary(code));
2819+
}
2820+
2821+
switch (op.type) {
2822+
case TokenType.TILDE_SLASH:
2823+
// `a ~/ b` is equivalent to `(a / b).truncate()`
2824+
var div = AstBuilder.binaryExpression(left, '/', right)
2825+
..staticType = node.staticType;
2826+
return _emitSend(div, 'truncate', []);
2827+
2828+
case TokenType.PERCENT:
2829+
// TODO(sra): We can generate `a % b + 0` if both are non-negative
2830+
// (the `+ 0` is to coerce -0.0 to 0).
2831+
return _emitSend(left, op.lexeme, [right]);
2832+
2833+
case TokenType.AMPERSAND:
2834+
return bitwise('# & #');
2835+
2836+
case TokenType.BAR:
2837+
return bitwise('# | #');
2838+
2839+
case TokenType.CARET:
2840+
return bitwise('# ^ #');
2841+
2842+
case TokenType.GT_GT:
2843+
// TODO(sra): Detect when JS shift does the right thing.
2844+
return _emitSend(left, op.lexeme, [right]);
2845+
2846+
case TokenType.LT_LT:
2847+
// TODO(sra): Detect when JS shift does the right thing.
2848+
return _emitSend(left, op.lexeme, [right]);
2849+
2850+
default:
2851+
// TODO(vsm): When do Dart ops not map to JS?
2852+
return binary('# $op #');
28212853
}
2822-
return js.call(code, [notNull(left), notNull(right)]);
28232854
}
28242855

28252856
return _emitSend(left, op.lexeme, [right]);
28262857
}
28272858

2859+
/// Bit operations are coerced to values on [0, 2^32). The coercion changes
2860+
/// the interpretation of the 32-bit value from signed to unsigned. Most
2861+
/// JavaScript operations interpret their operands as signed and generate
2862+
/// signed results.
2863+
JS.Expression _coerceBitOperationResultToUnsigned(
2864+
Expression node, JS.Expression operation) {
2865+
return js.call('# >>> 0', operation);
2866+
}
2867+
28282868
/// If the type [t] is [int] or [double], or a type parameter
28292869
/// bounded by [int], [double] or [num] returns [num].
28302870
/// Otherwise returns [t].
@@ -3001,9 +3041,17 @@ class CodeGenerator extends GeneralizingAstVisitor
30013041

30023042
var dispatchType = getStaticType(expr);
30033043
if (unaryOperationIsPrimitive(dispatchType)) {
3044+
if (op.lexeme == '~') {
3045+
if (_isNumberInJS(dispatchType)) {
3046+
JS.Expression jsExpr = js.call('~#', notNull(expr));
3047+
return _coerceBitOperationResultToUnsigned(node, jsExpr);
3048+
}
3049+
return _emitSend(expr, op.lexeme[0], []);
3050+
}
30043051
if (!isNullable(expr)) {
30053052
return js.call('$op#', _visit(expr));
3006-
} else if (op.lexeme == '++' || op.lexeme == '--') {
3053+
}
3054+
if (op.lexeme == '++' || op.lexeme == '--') {
30073055
// We need a null check, so the increment must be expanded out.
30083056
var vars = <JS.MetaLetVariable, JS.Expression>{};
30093057
var x = _bindLeftHandSide(vars, expr, context: expr);
@@ -3014,9 +3062,8 @@ class CodeGenerator extends GeneralizingAstVisitor
30143062
..staticType = getStaticType(expr);
30153063

30163064
return new JS.MetaLet(vars, [_emitSet(x, increment)]);
3017-
} else {
3018-
return js.call('$op#', notNull(expr));
30193065
}
3066+
return js.call('$op#', notNull(expr));
30203067
}
30213068

30223069
if (op.lexeme == '++' || op.lexeme == '--') {

pkg/dev_compiler/test/browser/language_tests.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
'bit_shift_test',
2626
'bool_test',
2727
'bound_closure_equality_test',
28+
'branch_canonicalization_test', // JS bit operations truncate to 32 bits.
2829
'call_closurization_test',
2930
'call_function_apply_test',
3031
'call_operator_test',

pkg/dev_compiler/test/codegen/expect/expect.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dart_library.library('expect', null, /* Imports */[
2626
if (dart.notNull(code) < 32) {
2727
buf.write("\\x");
2828
buf.write("0123456789abcdef"[dartx.get]((dart.notNull(code) / 16)[dartx.truncate]()));
29-
buf.write("0123456789abcdef"[dartx.get](dart.notNull(code) % 16));
29+
buf.write("0123456789abcdef"[dartx.get](code[dartx['%']](16)));
3030
} else {
3131
buf.writeCharCode(string[dartx.codeUnitAt](i));
3232
}

pkg/dev_compiler/test/codegen/expect/expect/expect.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dart_library.library('expect', null, /* Imports */[
2626
if (dart.notNull(code) < 32) {
2727
buf.write("\\x");
2828
buf.write("0123456789abcdef"[dartx.get]((dart.notNull(code) / 16)[dartx.truncate]()));
29-
buf.write("0123456789abcdef"[dartx.get](dart.notNull(code) % 16));
29+
buf.write("0123456789abcdef"[dartx.get](code[dartx['%']](16)));
3030
} else {
3131
buf.writeCharCode(string[dartx.codeUnitAt](i));
3232
}

pkg/dev_compiler/test/codegen/expect/notnull.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ dart_library.library('notnull', null, /* Imports */[
88
const notnull = Object.create(null);
99
notnull.intAssignments = function() {
1010
let i = 0;
11-
i = i & 1;
12-
i = i | 1;
13-
i = i ^ 1;
14-
i = i >> 1;
15-
i = i << 1;
11+
i = (i & 1) >>> 0;
12+
i = (i | 1) >>> 0;
13+
i = (i ^ 1) >>> 0;
14+
i = i[dartx['>>']](1);
15+
i = i[dartx['<<']](1);
1616
i = i - 1;
17-
i = i % 1;
17+
i = i[dartx['%']](1);
1818
i = i + 1;
1919
let t = i;
2020
t == null ? i = 1 : t;

0 commit comments

Comments
 (0)