summaryrefslogtreecommitdiff
path: root/erts/emulator/beam/jit/arm/instr_call.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/jit/arm/instr_call.cpp')
-rw-r--r--erts/emulator/beam/jit/arm/instr_call.cpp217
1 files changed, 217 insertions, 0 deletions
diff --git a/erts/emulator/beam/jit/arm/instr_call.cpp b/erts/emulator/beam/jit/arm/instr_call.cpp
new file mode 100644
index 0000000000..58bbd45a42
--- /dev/null
+++ b/erts/emulator/beam/jit/arm/instr_call.cpp
@@ -0,0 +1,217 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2020-2022. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "beam_asm.hpp"
+
+extern "C"
+{
+#include "beam_common.h"
+}
+
+void BeamGlobalAssembler::emit_dispatch_return() {
+ a.mov(ARG3, a64::x30);
+ a.str(ZERO, arm::Mem(c_p, offsetof(Process, current)));
+ mov_imm(TMP1, 1);
+ a.str(TMP1, arm::Mem(c_p, offsetof(Process, arity)));
+ a.b(labels[context_switch_simplified]);
+}
+
+void BeamModuleAssembler::emit_return() {
+ emit_leave_erlang_frame();
+
+#ifdef JIT_HARD_DEBUG
+ /* Validate return address and {x,0} */
+ emit_validate(ArgVal(ArgVal::Word, 1));
+#endif
+
+ /* The reduction test is kept in module code because moving it to a shared
+ * fragment caused major performance regressions in dialyzer. */
+ a.subs(FCALLS, FCALLS, imm(1));
+ a.b_mi(resolve_fragment(ga->get_dispatch_return(), disp1MB));
+
+ a.ret(a64::x30);
+}
+
+void BeamModuleAssembler::emit_i_call(const ArgLabel &CallTarget) {
+ erlang_call(resolve_beam_label(CallTarget, disp128MB));
+}
+
+void BeamModuleAssembler::emit_i_call_last(const ArgLabel &CallTarget,
+ const ArgWord &Deallocate) {
+ emit_deallocate(Deallocate);
+ emit_i_call_only(CallTarget);
+}
+
+void BeamModuleAssembler::emit_i_call_only(const ArgLabel &CallTarget) {
+ emit_leave_erlang_frame();
+ a.b(resolve_beam_label(CallTarget, disp128MB));
+}
+
+/* Handles save_calls. When the active code index is ERTS_SAVE_CALLS_CODE_IX,
+ * all remote calls will land here.
+ *
+ * Export entry is in ARG1, return address is in LR (x30). Both of these must
+ * be preserved since this runs between caller and callee. */
+void BeamGlobalAssembler::emit_dispatch_save_calls() {
+ a.str(ARG1, TMP_MEM1q);
+
+ emit_enter_runtime_frame();
+ emit_enter_runtime();
+
+ a.mov(ARG2, ARG1);
+ a.mov(ARG1, c_p);
+ runtime_call<2>(save_calls);
+
+ emit_leave_runtime();
+ emit_leave_runtime_frame();
+
+ a.ldr(ARG1, TMP_MEM1q);
+
+ /* Keep going with the actual code index. */
+ a.mov(TMP1, imm(&the_active_code_index));
+ a.ldr(TMP1.w(), arm::Mem(TMP1));
+
+ branch(emit_setup_dispatchable_call(ARG1, TMP1));
+}
+
+void BeamModuleAssembler::emit_i_call_ext(const ArgExport &Exp) {
+ mov_arg(ARG1, Exp);
+
+ arm::Mem target = emit_setup_dispatchable_call(ARG1);
+ erlang_call(target);
+}
+
+void BeamModuleAssembler::emit_i_call_ext_only(const ArgExport &Exp) {
+ mov_arg(ARG1, Exp);
+
+ arm::Mem target = emit_setup_dispatchable_call(ARG1);
+ emit_leave_erlang_frame();
+ branch(target);
+}
+
+void BeamModuleAssembler::emit_i_call_ext_last(const ArgExport &Exp,
+ const ArgWord &Deallocate) {
+ emit_deallocate(Deallocate);
+ emit_i_call_ext_only(Exp);
+}
+
+static ErtsCodeMFA apply3_mfa = {am_erlang, am_apply, 3};
+
+arm::Mem BeamModuleAssembler::emit_variable_apply(bool includeI) {
+ Label dispatch = a.newLabel(), entry = a.newLabel();
+
+ a.bind(entry);
+
+ emit_enter_runtime<Update::eReductions | Update::eStack | Update::eHeap |
+ Update::eXRegs>(3);
+
+ a.mov(ARG1, c_p);
+ load_x_reg_array(ARG2);
+
+ if (includeI) {
+ a.adr(ARG3, entry);
+ } else {
+ mov_imm(ARG3, 0);
+ }
+
+ mov_imm(ARG4, 0);
+
+ comment("apply()");
+ runtime_call<4>(apply);
+
+ /* Any number of X registers can be live at this point. */
+ emit_leave_runtime<Update::eReductions | Update::eStack | Update::eHeap |
+ Update::eXRegs>();
+
+ a.cbnz(ARG1, dispatch);
+ emit_raise_exception(entry, &apply3_mfa);
+
+ a.bind(dispatch);
+ return emit_setup_dispatchable_call(ARG1);
+}
+
+void BeamModuleAssembler::emit_i_apply() {
+ arm::Mem target = emit_variable_apply(false);
+ erlang_call(target);
+}
+
+void BeamModuleAssembler::emit_i_apply_last(const ArgWord &Deallocate) {
+ emit_deallocate(Deallocate);
+ emit_i_apply_only();
+}
+
+void BeamModuleAssembler::emit_i_apply_only() {
+ arm::Mem target = emit_variable_apply(true);
+
+ emit_leave_erlang_frame();
+ branch(target);
+}
+
+arm::Mem BeamModuleAssembler::emit_fixed_apply(const ArgWord &Arity,
+ bool includeI) {
+ Label dispatch = a.newLabel(), entry = a.newLabel();
+
+ a.bind(entry);
+
+ mov_arg(ARG3, Arity);
+
+ emit_enter_runtime<Update::eReductions | Update::eStack | Update::eHeap |
+ Update::eXRegs>(Arity.get() + 2);
+
+ a.mov(ARG1, c_p);
+ load_x_reg_array(ARG2);
+
+ if (includeI) {
+ a.adr(ARG4, entry);
+ } else {
+ mov_imm(ARG4, 0);
+ }
+
+ mov_imm(ARG5, 0);
+
+ runtime_call<5>(fixed_apply);
+
+ /* We will need to reload all X registers in case there has been
+ * an error. */
+ emit_leave_runtime<Update::eReductions | Update::eStack | Update::eHeap |
+ Update::eXRegs>();
+
+ a.cbnz(ARG1, dispatch);
+ emit_raise_exception(entry, &apply3_mfa);
+
+ a.bind(dispatch);
+
+ return emit_setup_dispatchable_call(ARG1);
+}
+
+void BeamModuleAssembler::emit_apply(const ArgWord &Arity) {
+ arm::Mem target = emit_fixed_apply(Arity, false);
+ erlang_call(target);
+}
+
+void BeamModuleAssembler::emit_apply_last(const ArgWord &Arity,
+ const ArgWord &Deallocate) {
+ emit_deallocate(Deallocate);
+
+ arm::Mem target = emit_fixed_apply(Arity, true);
+
+ emit_leave_erlang_frame();
+ branch(target);
+}