Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes the Clang compiler caused deadloop issue #4530

Closed

Conversation

lygstate
Copy link
Contributor

Clang mis-optimization tail calling to ecma_op_object_get_with_receiver:
When ecma_op_object_get_with_receiver are called, the stack size will not increase.

That's cause testcase tests/jerry/es.next/regression-test-issue-3785.js to be deadloop
instead of stack overflow exception.

JerryScript-DCO-1.0-Signed-off-by: Yonggang Luo [email protected]

@lygstate
Copy link
Contributor Author

Here is the full description of the bug:
https://lists.llvm.org/pipermail/llvm-dev/2021-January/148017.html

@lygstate lygstate force-pushed the fix-clang-deadloop branch 3 times, most recently from 5d7b4a3 to 96aa837 Compare January 23, 2021 14:00
When clang do tail optimization calling to ecma_op_object_get_with_receiver,
the stack size will not increase.
That's cause testcase tests/jerry/es.next/regression-test-issue-3785.js to be deadloop
instead of stack overflow exception.

Use -fno-optimize-sibling-calls to disable the tail optimization.

JerryScript-DCO-1.0-Signed-off-by: Yonggang Luo [email protected]
@ossy-szeged
Copy link
Contributor

I think it isn't a bug in clang. Maybe a bug in jerryscript or a bug in the testcase.
Maybe @dbatyai could help to clarify if the spec says anything about this situation.
( He added tests/jerry/es.next/regression-test-issue-3785.js to fix #3785 in #3796 )

@lygstate
Copy link
Contributor Author

Maybe @dbatyai could help to clarify if the spec says anything about this situation.

Yeap, it's not a bug of clang, I've verified that.

@lygstate
Copy link
Contributor Author

lygstate commented Jan 28, 2021

Maybe we need use something like this to disable tail-optimization:

https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+/HEAD/absl/base/optimization.h#29


// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
//
// Instructs the compiler to avoid optimizing tail-call recursion. This macro is
// useful when you wish to preserve the existing function order within a stack
// trace for logging, debugging, or profiling purposes.
//
// Example:
//
//   int f() {
//     int result = g();
//     ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
//     return result;
//   }
#if defined(__pnacl__)
#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
#elif defined(__clang__)
// Clang will not tail call given inline volatile assembly.
#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
#elif defined(__GNUC__)
// GCC will not tail call given inline volatile assembly.
#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
#elif defined(_MSC_VER)
#include <intrin.h>
// The __nop() intrinsic blocks the optimisation.
#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop()
#else
#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
#endif


@zherczeg
Copy link
Member

Please provide a backtrace so we can understand what is happening here.

@lygstate
Copy link
Contributor Author

Here is the backtrace

 jerry-core/ecma/operations/ecma-proxy-object.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c
b/jerry-core/ecma/operations/ecma-proxy-object.c
index c5d299c6..77aed25c 100644
--- a/jerry-core/ecma/operations/ecma-proxy-object.c
+++ b/jerry-core/ecma/operations/ecma-proxy-object.c
@@ -1108,7 +1108,9 @@ ecma_proxy_object_get (ecma_object_t *obj_p, /**<
proxy object */
   if (ecma_is_value_undefined (trap))
   {
     ecma_object_t *target_obj_p = ecma_get_object_from_value
(proxy_obj_p->target);
-    return ecma_op_object_get_with_receiver (target_obj_p, prop_name_p,
receiver);
+    ecma_value_t value = ecma_op_object_get_with_receiver (target_obj_p,
prop_name_p, receiver);
+    fflush (stdout);
+    return value;
   }

   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);

In which condition, ecma_op_object_get_with_receiver tail calling will not
increase the stack-size?
I have a test-case that cause infinite loop, because that in normal
conition, it's expect to raise an stack-exceed error but
now with llvm-compiled release build binary, it's turn out to be infinite
loop because ecma_op_object_get_with_receiver
will not increase the stack size anymore, once
ecma_op_object_get_with_receiver getting called, it's returned to the
previous stack position。
Reproduced on OSX/Ubuntu 20.04

Stack-trace-base

jerry!ecma_proxy_object_get
(\Users\lygstate\work\typescript\jerryscript\jerry-core\ecma\operations\ecma-proxy-object.c:1111)
jerry!ecma_op_object_get_with_receiver
(\Users\lygstate\work\typescript\jerryscript\jerry-core\ecma\operations\ecma-objects.c:790)
jerry!ecma_op_object_get
(\Users\lygstate\work\typescript\jerryscript\jerry-core\ecma\operations\ecma-objects.c:763)
jerry!vm_op_get_value
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:114)
jerry!vm_loop
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:2799)
jerry!vm_execute
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:4939)
jerry!vm_run
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:5046)
jerry!vm_run_global
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:328)
jerry!jerry_run
(\Users\lygstate\work\typescript\jerryscript\jerry-core\api\jerry.c:671)
jerry!main
(\Users\lygstate\work\typescript\jerryscript\jerry-main\main-unix.c:156)
libdyld.dylib!start (Unknown Source:0)
libdyld.dylib!start (Unknown Source:0)

stack-trace-new, it's hope ecma_op_object_get_with_receiver to deeper, but
turn out to step-back.

jerry!ecma_op_object_get_with_receiver
(\Users\lygstate\work\typescript\jerryscript\jerry-core\ecma\operations\ecma-objects.c:784)
jerry!ecma_op_object_get
(\Users\lygstate\work\typescript\jerryscript\jerry-core\ecma\operations\ecma-objects.c:763)
jerry!vm_op_get_value
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:114)
jerry!vm_loop
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:2799)
jerry!vm_execute
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:4939)
jerry!vm_run
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:5046)
jerry!vm_run_global
(\Users\lygstate\work\typescript\jerryscript\jerry-core\vm\vm.c:328)
jerry!jerry_run
(\Users\lygstate\work\typescript\jerryscript\jerry-core\api\jerry.c:671)
jerry!main
(\Users\lygstate\work\typescript\jerryscript\jerry-main\main-unix.c:156)
libdyld.dylib!start (Unknown Source:0)
libdyld.dylib!start (Unknown Source:0)

Same clang , build it with debug flag, the issue gone. I am wonder if there
is a misoptimization here

The c code
https://gist.github.com/lygstate/b554e74a22353d50a24240128f875474

The full source tree
https://github.com/lygstate/jerryscript/tree/osx-clang-bug

@rerobika rerobika added the ES.next Related to ES2015+ features label Feb 4, 2021
@rerobika
Copy link
Member

rerobika commented Feb 4, 2021

@galpeter could you please share your progress about this issue? AFAIK you also tried some other approach.

@galpeter
Copy link
Contributor

galpeter commented Feb 4, 2021

yeah I have a way to resolve this. The macros @lygstate mentioned were used to add the tail call disable at given points and also added extra stack checks in the Proxy internal methods. Will upload soon. Note: this is not a clang specific thing and is a very edge case to construct a proxy which recurses (infinite times).

@lygstate
Copy link
Contributor Author

lygstate commented Feb 6, 2021

Close as proper fix exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ES.next Related to ES2015+ features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants