Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions src/generators/genlua.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ and gen_expr ?(local=true) ctx e = begin
spr ctx "not ";
gen_value ctx e;
| TUnop (NegBits,unop_flag,e) ->
add_feature ctx "use._bitop";
add_feature ctx "op_bitwise";
spr ctx "_hx_bit.bnot(";
gen_value ctx e;
spr ctx ")";
Expand Down Expand Up @@ -1572,7 +1572,7 @@ and gen_paren_tbinop ctx e =
gen_value ctx ee

and gen_bitop ctx op e1 e2 =
add_feature ctx "use._bitop";
add_feature ctx "op_bitwise";
print ctx "_hx_bit.%s(" (match op with
| Ast.OpXor -> "bxor"
| Ast.OpAnd -> "band"
Expand Down Expand Up @@ -2215,21 +2215,18 @@ let generate com =
List.iter (generate_type_forward ctx) com.types; newline ctx;

(* Generate some dummy placeholders for utility libs that may be required*)
println ctx "local _hx_bind, _hx_bit, _hx_staticToInstance, _hx_funcToField, _hx_anonToField, _hx_maxn, _hx_print, _hx_apply_self, _hx_box_mr, _hx_bit_clamp, _hx_table, _hx_bit_raw";
println ctx "local _hx_bind, _hx_bit, _hx_staticToInstance, _hx_funcToField, _hx_anonToField, _hx_maxn, _hx_print, _hx_apply_self, _hx_box_mr, _hx_table, _hx_bit_raw";
println ctx "local _hx_pcall_default = {};";
println ctx "local _hx_pcall_break = {};";

List.iter (transform_multireturn ctx) com.types;
List.iter (generate_type ctx) com.types;

(* If bit ops are manually imported include the haxe wrapper for them *)
if has_feature ctx "use._bitop" then begin
if has_feature ctx "op_bitwise" then begin
print_file (find_file "lua/_lua/_hx_bit.lua");
end;

(* integer clamping is always required, and will use bit ops if available *)
print_file (find_file "lua/_lua/_hx_bit_clamp.lua");

(* Array is required, always patch it *)
println ctx "_hx_array_mt.__index = Array.prototype";
newline ctx;
Expand Down
6 changes: 6 additions & 0 deletions src/optimization/dce.ml
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,9 @@ and check_op dce op = match op with
check_and_add_feature dce "binop_%";
| OpUShr ->
check_and_add_feature dce "binop_>>>";
check_and_add_feature dce "op_bitwise";
| OpXor | OpOr | OpAnd | OpShl | OpShr ->
check_and_add_feature dce "op_bitwise";
| OpAssignOp op ->
check_op dce op
| _ ->
Expand Down Expand Up @@ -708,6 +711,9 @@ and expr dce e =
check_op dce op;
expr dce e1;
expr dce e2;
| TUnop(NegBits,flag,e) ->
check_and_add_feature dce "op_bitwise";
expr dce e;
| TCall(({ eexpr = TField(ef, fa) } as e2), el ) ->
mark_t dce e2.epos e2.etype;
expr_field dce ef fa true;
Expand Down
2 changes: 1 addition & 1 deletion std/lua/Bit.hx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ extern class Bit {
static function arshift(x:Float, places:Int):Int;
static function mod(numerator:Float, denominator:Float):Int;
static function __init__():Void {
untyped _hx_bit = __define_feature__("use._bitop", _hx_bit);
untyped _hx_bit = __define_feature__("op_bitwise", _hx_bit);
}
}
88 changes: 84 additions & 4 deletions std/lua/Boot.hx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,28 @@ package lua;
import haxe.SysTools;

@:dox(hide)
@:noPackageRestrict
class Boot {
// wrap with common clamping functionality for all implementations
macro static function clampWrapper(inner:haxe.macro.Expr):haxe.macro.Expr {
return macro function(v:Float):Int {
if (v <= Max_Int32 && v >= Min_Int32)
return v > 0 ? std.Math.floor(v) : std.Math.ceil(v);

if (inline std.Math.isNaN(v) || !inline std.Math.isFinite(v))
return null;

return $inner(v);
};
}

#if !macro
// Used temporarily for bind()
static var _:Dynamic;
static var _fid = 0;

static var Max_Int32 = 2147483647;
static var Min_Int32 = -2147483648;
static inline var Max_Int32 = 2147483647;
static inline var Min_Int32 = -2147483648;


// A max stack size to respect for unpack operations
Expand Down Expand Up @@ -194,11 +209,75 @@ class Boot {
+ (if (h < 10) "0" + h else "" + h) + ":" + (if (mi < 10) "0" + mi else "" + mi) + ":" + (if (s < 10) "0" + s else "" + s);
}


extern inline static function prepareForBitwise(v:Float) {
if (v > 2251798999999999) {
return v * 2;
}
return v;
}

extern inline static function clampNativeOperator(v:Float):Int {
v = prepareForBitwise(v);
return lua.Syntax.code("({0} & 0x7FFFFFFF) - ({0} & 0x80000000)", v);
}

extern inline static function clampHxBit(v:Float):Int {
v = prepareForBitwise(v);
final band:(Float, Float) -> Int = untyped _hx_bit_raw.band;
return band(v, Max_Int32) - cast Math.abs(band(v, 2147483648));
}

extern inline static function clampModulo(v:Float):Int {
v = lua.Syntax.modulo(v, 4294967296);
if (v >= 2147483648) {
v -= 4294967296;
}
return cast v;
}

/**
Test whether the syntax used in function `f` is supported on the current lua version.

If it is, return it, otherwise return null.
**/
extern inline static function testFunctionSupport<T, S>(f:T->S):Null<T->S> {
final result = Lua.pcall(Lua.load, lua.Syntax.code("[[return {0}]]", f));
if (result.status && result.value != null) {
final fn:() -> (T->S) = result.value;
return fn();
}
return null;
}

/**
A 32 bit clamp function for numbers
**/
public inline static function clampInt32(x:Float) {
return untyped _hx_bit_clamp(x);
@:ifFeature("op_bitwise")
public static function clampInt32(v:Float) {
#if (lua_ver >= 5.3)
return clampWrapper(clampNativeOperator)(v);
#else
final clampImpl = {
// Try native Lua 5.3+ bit operators first (preferred over bit32/bit library)
final nativeOperators = testFunctionSupport(clampWrapper(clampNativeOperator));
if (nativeOperators != null) {
nativeOperators;
#if !(lua_ver >= 5.2) // lua 5.2 definitely has bit32
} else if (untyped _hx_bit_raw == null) {
// Fallback for Lua without bit, bit32, or native bit ops: wrap using modulo
clampWrapper(clampModulo);
#end
} else {
clampWrapper(clampHxBit);
}
};

// set implementation so that future calls don't have to perform detection
untyped lua.Boot.clampInt32 = clampImpl;

return clampImpl(v);
#end
}

/**
Expand Down Expand Up @@ -316,4 +395,5 @@ class Boot {

return null;
}
#end
}
9 changes: 9 additions & 0 deletions std/lua/Syntax.hx
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,13 @@ extern class Syntax {
The same as `lua.Syntax.code` except this one does not provide code interpolation.
**/
static function plainCode(code:String):Dynamic;

/**
Generates a native modulo expression.

Modulo is defined as the remainder of a division that rounds the quotient towards minus infinity (floor division).
**/
inline static function modulo(a:Float, b:Float):Float {
return code("({0} % {1})", a, b);
}
}
12 changes: 6 additions & 6 deletions std/lua/_lua/_hx_bit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ if _G.bit32 or pcall(require, 'bit32') then
_hx_bit_raw = _G.bit32 or require('bit32')
_hx_bit = setmetatable({}, { __index = _hx_bit_raw })
-- bit32 operations require manual clamping
_hx_bit.bnot = function(...) return _hx_bit_clamp(_hx_bit_raw.bnot(...)) end
_hx_bit.bxor = function(...) return _hx_bit_clamp(_hx_bit_raw.bxor(...)) end
_hx_bit.bnot = function(...) return __lua_Boot.clampInt32(_hx_bit_raw.bnot(...)) end
_hx_bit.bxor = function(...) return __lua_Boot.clampInt32(_hx_bit_raw.bxor(...)) end
-- see https://github.com/HaxeFoundation/haxe/issues/8849
_hx_bit.bor = function(...) return _hx_bit_clamp(_hx_bit_raw.bor(...)) end
_hx_bit.band = function(...) return _hx_bit_clamp(_hx_bit_raw.band(...)) end
_hx_bit.arshift = function(...) return _hx_bit_clamp(_hx_bit_raw.arshift(...)) end
_hx_bit.lshift = function(...) return _hx_bit_clamp(_hx_bit_raw.lshift(...)) end
_hx_bit.bor = function(...) return __lua_Boot.clampInt32(_hx_bit_raw.bor(...)) end
_hx_bit.band = function(...) return __lua_Boot.clampInt32(_hx_bit_raw.band(...)) end
_hx_bit.arshift = function(...) return __lua_Boot.clampInt32(_hx_bit_raw.arshift(...)) end
_hx_bit.lshift = function(...) return __lua_Boot.clampInt32(_hx_bit_raw.lshift(...)) end
elseif _G.bit or pcall(require, 'bit') then
-- if we do not have bit32, fallback to bit, default on luajit
_hx_bit_raw = _G.bit or require('bit')
Expand Down
49 changes: 0 additions & 49 deletions std/lua/_lua/_hx_bit_clamp.lua

This file was deleted.

2 changes: 1 addition & 1 deletion std/lua/_std/Array.hx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Array<T> {
public function reverse():Void {
var tmp:T;
var i = 0;
while (i < Std.int(this.length / 2)) {
while (i < Math.floor(this.length / 2)) {
tmp = this[i];
this[i] = this[this.length - i - 1];
this[this.length - i - 1] = tmp;
Expand Down
22 changes: 13 additions & 9 deletions tests/misc/lua/projects/Issue11013/Main.hx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
class Main {
static function main() {
// Test Int32 wrapping without bit32/bit library available.
// require, bit32, and bit are set to nil before running, so they cannot be used
var max:haxe.Int32 = 2147483647;
var one:haxe.Int32 = 1;
var result:Int = max + one;
var expected = -2147483648;
if (result == -2147483648) {
static function assertEquals(expected, actual) {
if (actual == expected) {
Sys.println("Success");
} else {
Sys.println('Expected $expected but got $result');
Sys.println('Expected $expected but got $actual');
}
}

static function main() {
// Test Int32 wrapping without bit32/bit library available.
// require, bit32, and bit are set to nil before running, so they cannot be used
final max:haxe.Int32 = 2147483647;
final min:haxe.Int32 = -2147483648;
final one:haxe.Int32 = 1;
assertEquals(min, max + one);
assertEquals(max, min - one);
}
}
3 changes: 2 additions & 1 deletion tests/misc/lua/projects/Issue11013/compile.hxml.stdout
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Success
Success
Success
Loading