Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
9 changes: 2 additions & 7 deletions src/generators/genlua.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,6 @@ 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";
spr ctx "_hx_bit.bnot(";
gen_value ctx e;
spr ctx ")";
Expand Down Expand Up @@ -1572,7 +1571,6 @@ and gen_paren_tbinop ctx e =
gen_value ctx ee

and gen_bitop ctx op e1 e2 =
add_feature ctx "use._bitop";
print ctx "_hx_bit.%s(" (match op with
| Ast.OpXor -> "bxor"
| Ast.OpAnd -> "band"
Expand Down Expand Up @@ -2215,21 +2213,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);
}
}
87 changes: 83 additions & 4 deletions std/lua/Boot.hx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class Boot {
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 +194,90 @@ class Boot {
+ (if (h < 10) "0" + h else "" + h) + ":" + (if (mi < 10) "0" + mi else "" + mi) + ":" + (if (s < 10) "0" + s else "" + s);
}

// wrap with common functionality for all implementations
extern inline static function clampWrapper(inner:Float->Null<Int>):Float->Null<Int> {
return function (v:Float) {
if (v <= Max_Int32 && v >= Min_Int32)
return v > 0 ? Math.floor(v) : Math.ceil(v);

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

return inner(v);
};
}

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

// Ideally, these should be extern inline because they should only be used
// via inlining (we also don't want clampNativeOperator outside testFunctionSupport)
// however, extern inline prevents closures, even if they are immediately inlined
Comment thread
tobil4sk marked this conversation as resolved.
Outdated
inline static function clampNativeOperator(v:Float) {
v = prepareForBitwise(v);
return lua.Syntax.code("({0} & 0x7FFFFFFF) - ({0} & 0x80000000)", v);
}

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));
}

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