diff --git a/examples/fibonacci.cc b/examples/fibonacci.cc index c380c5d..3f050b0 100644 --- a/examples/fibonacci.cc +++ b/examples/fibonacci.cc @@ -51,14 +51,11 @@ jasmin::Program FibonacciRecursive() { } struct UpdateFibonacci : jasmin::Instruction { - static void consume(std::span values, - std::span out) { - auto n = values[0]; - auto a = values[1]; - auto b = values[2]; - out[0] = static_cast(n.as() - 1); - out[1] = b; - out[2] = a.as() + b.as(); + static void consume(jasmin::Input in, + jasmin::Output out) { + out.set<0>(in.get<0>() - 1); + out.set<1>(in.get<2>()); + out.set<2>(in.get<1>() + in.get<2>()); } }; diff --git a/examples/function_state.cc b/examples/function_state.cc index 95de7bb..ec76259 100644 --- a/examples/function_state.cc +++ b/examples/function_state.cc @@ -10,9 +10,8 @@ // before this one. struct PrintCString : jasmin::Instruction { - static void consume(std::span values, - std::span) { - std::fputs(values[0].as(), stdout); + static void consume(jasmin::Input in, jasmin::Output<>) { + std::fputs(in.get<0>(), stdout); } }; @@ -31,8 +30,8 @@ struct Queue { struct PushQueue : jasmin::Instruction { using function_state = Queue; - static void execute(function_state &state, std::span, - std::span, char const *cstr) { + static void execute(function_state &state, jasmin::Input<>, jasmin::Output<>, + char const *cstr) { state.queue.push(cstr); } }; @@ -40,17 +39,17 @@ struct PushQueue : jasmin::Instruction { struct PopQueue : jasmin::Instruction { using function_state = Queue; - static void execute(function_state &state, std::span, - std::span out) { - out[0] = state.queue.front(); + static void execute(function_state &state, jasmin::Input<>, + jasmin::Output out) { + out.set<0>(state.queue.front()); state.queue.pop(); } }; struct RotateQueue : jasmin::Instruction { using function_state = Queue; - static void execute(function_state &state, std::span, - std::span) { + static void execute(function_state &state, jasmin::Input<>, + jasmin::Output<>) { char const *top = state.queue.front(); state.queue.pop(); state.queue.push(top); diff --git a/examples/hello_world.cc b/examples/hello_world.cc index 50ce46f..14f6b1c 100644 --- a/examples/hello_world.cc +++ b/examples/hello_world.cc @@ -18,9 +18,8 @@ struct PrintCString : jasmin::Instruction { // Jasmin knows that exactly one value should be popped off the stack, and // that that one value is a `const char *` because it inspects the parameters // of the function named `consume`. - static void consume(std::span values, - std::span) { - std::fputs(values[0].as(), stdout); + static void consume(jasmin::Input in, jasmin::Output<>) { + std::fputs(in.get<0>(), stdout); } }; @@ -66,14 +65,13 @@ struct ReadIntegerFromStdIn : jasmin::Instruction { // Just as with `PrintCString`, from the signature of `execute`, Jasmin // deduces that this instruction reads no values from the stack but writes a // single value of type `int` (the return type). - static void execute(std::span, - std::span out) { + static void execute(jasmin::Input<>, jasmin::Output out) { int result; std::scanf("%d", &result); // Note: This function is not robust as it does nothing to validate that the // provided value was a valid integer. The author of an instruction is // responsible for handling this sort of error-checking for themselves. - out[0] = result; + out.set<0>(result); } }; diff --git a/jasmin/core/BUILD b/jasmin/core/BUILD index 186bb99..b55edb7 100644 --- a/jasmin/core/BUILD +++ b/jasmin/core/BUILD @@ -38,11 +38,33 @@ cc_test( ], ) +cc_library( + name = "input", + hdrs = ["input.h"], + visibility = ["//visibility:public"], + deps = [ + ":value", + "@nth_cc//nth/meta:sequence", + "@nth_cc//nth/meta:type", + ], +) + +cc_test( + name = "input_test", + srcs = ["input_test.cc"], + deps = [ + ":input", + "@nth_cc//nth/test:main", + ], +) + cc_library( name = "instruction", hdrs = ["instruction.h"], visibility = ["//visibility:public"], deps = [ + ":input", + ":output", ":value", "//jasmin/core/internal:function_base", "//jasmin/core/internal:function_state", @@ -97,6 +119,26 @@ cc_test( ], ) +cc_library( + name = "output", + hdrs = ["output.h"], + visibility = ["//visibility:public"], + deps = [ + ":value", + "@nth_cc//nth/meta:sequence", + "@nth_cc//nth/meta:type", + ], +) + +cc_test( + name = "output_test", + srcs = ["output_test.cc"], + deps = [ + ":output", + "@nth_cc//nth/test:main", + ], +) + cc_library( name = "program", hdrs = ["program.h"], diff --git a/jasmin/core/function_test.cc b/jasmin/core/function_test.cc index 007fe15..56d2ff1 100644 --- a/jasmin/core/function_test.cc +++ b/jasmin/core/function_test.cc @@ -6,9 +6,8 @@ namespace jasmin { namespace { struct PushImmediateBool : jasmin::Instruction { - static constexpr void execute(std::span, std::span out, - bool b) { - out[0] = b; + static constexpr void execute(Input<>, Output out, bool b) { + out.set<0>(b); } }; diff --git a/jasmin/core/input.h b/jasmin/core/input.h index 1dc9194..fd6a197 100644 --- a/jasmin/core/input.h +++ b/jasmin/core/input.h @@ -1,24 +1,44 @@ #ifndef JASMIN_CORE_INPUT_H #define JASMIN_CORE_INPUT_H +#include + +#include "jasmin/core/value.h" #include "nth/meta/sequence.h" #include "nth/meta/type.h" namespace jasmin { +namespace internal { +struct InputBase {}; +} // namespace internal template -struct in { +requires((std::constructible_from and ...)) // + struct Input : internal::InputBase { + static constexpr int count = sizeof...(Ts); + + explicit constexpr Input(Value const *ptr) : ptr_(ptr) {} + template - auto get() { - return (ptr_ + N * sizeof(Value)) - ->as()>>(); + constexpr auto get() { + return ptr_[N].as()>>(); } private: static constexpr auto types = nth::type_sequence; - Value *ptr_; + Value const *ptr_; }; } // namespace jasmin +template +struct std::tuple_size<::jasmin::Input> + : std::integral_constant {}; + +template +struct std::tuple_element> { + using type = + decltype(std::declval<::jasmin::Input>().template get()); +}; + #endif // JASMIN_CORE_INPUT_H diff --git a/jasmin/core/input_test.cc b/jasmin/core/input_test.cc new file mode 100644 index 0000000..5f9ff6f --- /dev/null +++ b/jasmin/core/input_test.cc @@ -0,0 +1,35 @@ +#include "jasmin/core/input.h" + +#include "nth/test/test.h" + +namespace jasmin { +namespace { + +static_assert(Input<>::count == 0); +static_assert(Input::count == 1); +static_assert(Input::count == 2); +static_assert(Input::count == 2); + +NTH_TEST("input/single") { + Value vs[1]; + vs[0] = 3; + Input input(vs); + NTH_EXPECT(input.get<0>() == 3); + auto [n] = input; + NTH_EXPECT(n == 3); +} + +NTH_TEST("input/multiple") { + Value vs[2]; + vs[0] = 3; + vs[1] = true; + Input input(vs); + NTH_EXPECT(input.get<0>() == 3); + NTH_EXPECT(input.get<1>()); + auto [n, b] = input; + NTH_EXPECT(n == 3); + NTH_EXPECT(b); +} + +} // namespace +} // namespace jasmin diff --git a/jasmin/core/instruction.h b/jasmin/core/instruction.h index 57ca9a6..3e60dc3 100644 --- a/jasmin/core/instruction.h +++ b/jasmin/core/instruction.h @@ -8,9 +8,11 @@ #include #include +#include "jasmin/core/input.h" #include "jasmin/core/internal/function_base.h" #include "jasmin/core/internal/function_state.h" #include "jasmin/core/internal/instruction_traits.h" +#include "jasmin/core/output.h" #include "jasmin/core/value.h" #include "nth/base/attributes.h" #include "nth/base/pack.h" @@ -57,15 +59,14 @@ struct Instruction { // If `typename Inst::function_state` is well-formed, the first parameter to // the function must be a reference to this type. // -// The function must then accept two span parameters -// `std::span` and `std::span`, where -// `In` and `Out` are the number of inputs and outputs processed by the -// instruction respectively. These may then be followed by any number of -// parameters whose types are convertible to `jasmin::Value`. These -// parameters are the immediate values to the function. Functions named -// consume will have their arguments popped from the stack after execution, -// whereas functions named `execute` will keep their arguments. The function -// must return void. +// The function must then accept a parameter of the form +// `jasmin::Input` followed by a parameter of the form +// `jasmin::Output` representing access to the inputs and outputs of +// the instruction respectively. These may then be followed by any number of +// parameters whose types are convertible to `jasmin::Value`. These parameters +// are the immediate values to the function. Functions named consume will have +// their arguments popped from the stack after execution, whereas functions +// named `execute` will keep their arguments. The function must return void. // // IMMEDIATE-VALUE-DETERMINED SIGNATURES: // If `typename Inst::function_state` is well-formed, the first parameter to @@ -93,6 +94,17 @@ concept InstructionType = (std::derived_from> and template concept InstructionSetType = std::derived_from; +// Returns `nth::type` where `F` is the type representing the function state +// associated with this instruction, or `nth::type` if the instruction +// does not manipulate any function state. +template +constexpr nth::Type auto FunctionState(); + +// Returns `true` if and only if the number of inputs/outputs processed by the +// function is determined by immediate values. +template +constexpr bool ImmediateValueDetermined(); + // Returns the number of immediate values passed to the instruction `I`. template constexpr size_t ImmediateValueCount(); @@ -307,10 +319,9 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, } else { constexpr auto inst = internal::InstructionFunctionPointer(); constexpr auto inst_type = internal::InstructionFunctionType(); - constexpr bool HasFunctionState = internal::HasFunctionState; - if constexpr (internal::ImmediateValueDetermined( - inst_type.parameters() - .template drop())) { + constexpr bool HasFunctionState = + (FunctionState() != nth::type); + if constexpr (ImmediateValueDetermined()) { auto [ins, outs] = (ip + 1)->as(); // If we consume, we still need a place to move the inputs during // execution. If not, we might need extra space to push return values. In @@ -325,8 +336,8 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, Value *input; Value *output; if constexpr (ConsumesInput()) { - output = value_stack_head - ins; - input = value_stack_head - ins + outs; + output = value_stack_head - ins; + input = value_stack_head - ins + outs; std::memmove(input, output, sizeof(Value) * ins); } else { input = value_stack_head - ins; @@ -350,7 +361,7 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, JASMIN_CORE_INTERNAL_GET((ip + 2), Ns)...); } } - (std::make_index_sequence()>{}); + (std::make_index_sequence() - 1>{}); // Note: We subtract one above because we do not need to pass the // `InstructionSpecification`. We don't want this reflected in the return // of `ImmediateValueCount` since it is technically still an immediate @@ -374,8 +385,8 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, nth::type_t()>; using output_type = nth::type_t< inst_type.parameters().template get<1 + HasFunctionState>()>; - constexpr size_t InputCount = input_type::extent; - constexpr size_t OutputCount = output_type::extent; + constexpr size_t InputCount = ParameterCount(); + constexpr size_t OutputCount = ReturnCount(); if (vs_left + (ConsumesInput() ? InputCount : 0) < OutputCount) [[unlikely]] { NTH_ATTRIBUTE(tailcall) @@ -396,13 +407,11 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, Value input[InputCount]; std::memcpy(input, value_stack_head - InputCount, sizeof(Value) * InputCount); - inst(fn_state, std::span(input, InputCount), - output_type(out_start, OutputCount), + inst(fn_state, input_type(input), output_type(out_start), JASMIN_CORE_INTERNAL_GET((ip + 1), Ns)...); } else { - inst(fn_state, - input_type(value_stack_head - InputCount, InputCount), - output_type(value_stack_head, OutputCount), + inst(fn_state, input_type(value_stack_head - InputCount), + output_type(value_stack_head), JASMIN_CORE_INTERNAL_GET((ip + 1), Ns)...); } } @@ -414,12 +423,11 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, Value input[InputCount]; std::memcpy(input, value_stack_head - InputCount, sizeof(Value) * InputCount); - inst(std::span(input, InputCount), - output_type(out_start, OutputCount), + inst(input_type(input), output_type(out_start), JASMIN_CORE_INTERNAL_GET((ip + 1), Ns)...); } else { - inst(input_type(value_stack_head - InputCount, InputCount), - output_type(value_stack_head, OutputCount), + inst(input_type(value_stack_head - InputCount), + output_type(value_stack_head), JASMIN_CORE_INTERNAL_GET((ip + 1), Ns)...); } } @@ -444,6 +452,24 @@ void Instruction::ExecuteImpl(Value *value_stack_head, size_t vs_left, } } +template +constexpr nth::Type auto FunctionState() { + if constexpr (requires { typename I::function_state; }) { + return nth::type; + } else { + return nth::type; + } +} + +template +constexpr bool ImmediateValueDetermined() { + return not std::derived_from< + nth::type_t() + .parameters() + .template get() != nth::type>()>, + internal::InputBase>; +} + template constexpr size_t ImmediateValueCount() { if constexpr (nth::any_of) { @@ -452,7 +478,8 @@ constexpr size_t ImmediateValueCount() { return 1; } else { return internal::InstructionFunctionType().parameters().size() - - (internal::HasFunctionState ? 3 : 2); + (FunctionState() == nth::type ? 2 : 3) + + ImmediateValueDetermined(); } } @@ -465,13 +492,11 @@ constexpr size_t ParameterCount() { } else { constexpr auto parameters = internal::InstructionFunctionType().parameters(); - if constexpr (internal::ImmediateValueDetermined( - parameters - .template drop>())) { + if constexpr (ImmediateValueDetermined()) { return -1; } else { - return nth::type_t< - parameters.template get>()>::extent; + return nth::type_t() != + nth::type>()>::count; } } } @@ -494,13 +519,11 @@ constexpr size_t ReturnCount() { } else { constexpr auto parameters = internal::InstructionFunctionType().parameters(); - if constexpr (internal::ImmediateValueDetermined( - parameters - .template drop>())) { + if constexpr (ImmediateValueDetermined()) { return -1; } else { - return nth::type_t< - parameters.template get<1 + internal::HasFunctionState>()>::extent; + return nth::type_t() != nth::type)>()>::count; } } } diff --git a/jasmin/core/instruction_test.cc b/jasmin/core/instruction_test.cc index a706a28..19f358c 100644 --- a/jasmin/core/instruction_test.cc +++ b/jasmin/core/instruction_test.cc @@ -8,30 +8,27 @@ namespace { struct Count : Instruction { using function_state = int; - static void execute(function_state& state, std::span, - std::span out) { - out[0] = state++; + static void execute(function_state& state, Input<>, Output out) { + out.set<0>(state++); } }; struct NoImmediates : Instruction { - static void execute(std::span, std::span out) { - out[0] = 0; - } + static void execute(Input, Output out) { out.set<0>(0); } }; struct NoImmediatesOrValues : Instruction { - static void execute(std::span, std::span) {} + static void execute(Input<>, Output<>) {} }; struct SomeImmediates : Instruction { - static void execute(std::span, std::span, int, bool) {} + static void execute(Input<>, Output<>, int, bool) {} }; -struct ReturnsMultiple: Instruction { - static void consume(std::span, std::span out) { - out[0] = 1; - out[1] = 2; +struct ReturnsMultiple : Instruction { + static void consume(Input<>, Output out) { + out.set<0>(1); + out.set<1>(2); } }; diff --git a/jasmin/core/internal/BUILD b/jasmin/core/internal/BUILD index e4090ca..0600054 100644 --- a/jasmin/core/internal/BUILD +++ b/jasmin/core/internal/BUILD @@ -42,6 +42,8 @@ cc_library( hdrs = ["instruction_traits.h"], deps = [ ":function_state", + "//jasmin/core:input", + "//jasmin/core:output", "//jasmin/core:value", "@nth_cc//nth/meta:concepts", "@nth_cc//nth/meta:type", diff --git a/jasmin/core/internal/instruction_traits.h b/jasmin/core/internal/instruction_traits.h index e554062..910c46a 100644 --- a/jasmin/core/internal/instruction_traits.h +++ b/jasmin/core/internal/instruction_traits.h @@ -1,7 +1,9 @@ #ifndef JASMIN_CORE_INTERNAL_INSTRUCTION_TRAITS_H #define JASMIN_CORE_INTERNAL_INSTRUCTION_TRAITS_H +#include "jasmin/core/input.h" #include "jasmin/core/internal/function_state.h" +#include "jasmin/core/output.h" #include "jasmin/core/value.h" #include "nth/meta/concepts.h" #include "nth/meta/type.h" @@ -51,69 +53,78 @@ constexpr bool ImmediateValueDetermined(nth::Sequence auto seq) { } constexpr bool ValidParameterSequence(nth::Sequence auto seq) { - if constexpr (seq.empty()) { + if constexpr (seq.size() < 2) { return false; } else { - using span_type = nth::type_t()>; - if constexpr (not requires { span_type::extent; }) { - return false; - } else if constexpr (seq.template get<0>() != - nth::type>) { - return false; - } else { + if constexpr (std::derived_from()>, + internal::InputBase> and + std::derived_from()>, + internal::OutputBase>) { return seq.template drop<1>().template all<[](auto t) { return std::is_convertible_v, Value>; }>(); + } else if constexpr (seq.template get<0>() == + nth::type> and + seq.template get<1>() == nth::type>) { + return seq.template drop<1>().template all<[](auto t) { + return std::is_convertible_v, Value>; + }>(); + } else { + return false; } } } -constexpr bool ValidSignatureWithFunctionStateImpl( - nth::Type auto body_type, nth::Type auto /*function_state_type*/) { - if constexpr (body_type.return_type() == nth::type) { - return ImmediateValueDetermined( - body_type.parameters().template drop<1>()) or - ValidParameterSequence(body_type.parameters().template drop<1>()); +template +constexpr auto InstructionFunctionType() { + if constexpr (nth::type == nth::type) { + return nth::type, InstructionSpecification)>; + } else if constexpr (nth::type == nth::type) { + return nth::type, ptrdiff_t)>; + } else if constexpr (nth::type == nth::type) { + return nth::type, ptrdiff_t)>; + } else if constexpr (nth::type == nth::type) { + return nth::type)>; } else { - return false; + if constexpr (requires { &I::consume; }) { + static_assert(std::is_function_v); + return nth::type; + } else { + static_assert(std::is_function_v); + return nth::type; + } } } -constexpr bool ValidSignatureWithoutFunctionStateImpl( - nth::Type auto body_type) { - if constexpr (body_type.return_type() == nth::type) { - return ImmediateValueDetermined(body_type.parameters()) or - ValidParameterSequence(body_type.parameters()); +template +constexpr auto InstructionFunctionPointer() { + if constexpr (requires { &I::consume; }) { + return &I::consume; } else { - return false; + return &I::execute; } } template constexpr bool ValidSignatureWithFunctionState() { - if constexpr (requires { &I::execute; }) { - return std::is_function_v and - ValidSignatureWithFunctionStateImpl( - nth::type, - nth::type); + auto body_type = InstructionFunctionType(); + if constexpr (body_type.return_type() == nth::type) { + return ImmediateValueDetermined( + body_type.parameters().template drop<1>()) or + ValidParameterSequence(body_type.parameters().template drop<1>()); } else { - return std::is_function_v and - ValidSignatureWithFunctionStateImpl( - nth::type, - nth::type); + return false; } } template constexpr bool ValidSignatureWithoutFunctionState() { - if constexpr (requires { &I::execute; }) { - return std::is_function_v and - ValidSignatureWithoutFunctionStateImpl( - nth::type); + auto body_type = InstructionFunctionType(); + if constexpr (body_type.return_type() == nth::type) { + return ImmediateValueDetermined(body_type.parameters()) or + ValidParameterSequence(body_type.parameters()); } else { - return std::is_function_v and - ValidSignatureWithoutFunctionStateImpl( - nth::type); + return false; } } @@ -122,30 +133,6 @@ concept UserDefinedInstruction = HasExactlyOneOfConsumeOrExecute() and ((HasFunctionState and ValidSignatureWithFunctionState()) or (not HasFunctionState and ValidSignatureWithoutFunctionState())); -template -constexpr auto InstructionFunctionPointer() { - if constexpr (requires { &I::consume; }) { - return &I::consume; - } else { - return &I::execute; - } -} - -template -constexpr auto InstructionFunctionType() { - if constexpr (nth::type == nth::type) { - return nth::type, InstructionSpecification)>; - } else if constexpr (nth::type == nth::type) { - return nth::type, ptrdiff_t)>; - } else if constexpr (nth::type == nth::type) { - return nth::type, ptrdiff_t)>; - } else if constexpr (nth::type == nth::type) { - return nth::type)>; - } else { - return nth::type())>.without_reference(); - } -} - } // namespace internal } // namespace jasmin diff --git a/jasmin/core/internal/instruction_traits_test.cc b/jasmin/core/internal/instruction_traits_test.cc index 3fd9adb..dbe37f0 100644 --- a/jasmin/core/internal/instruction_traits_test.cc +++ b/jasmin/core/internal/instruction_traits_test.cc @@ -7,171 +7,121 @@ namespace jasmin::internal { namespace { struct Nop { - static void execute(std::span); + static void execute(Input<>, Output<>); }; static_assert(UserDefinedInstruction); struct NopConsume { - static void consume(std::span); + static void consume(Input<>, Output<>); }; static_assert(UserDefinedInstruction); struct FunctionStateNop { using function_state = int; - static void execute(function_state&, std::span); + static void execute(function_state&, Input<>, Output<>); }; static_assert(UserDefinedInstruction); struct FunctionStateNopConsume { using function_state = int; - static void consume(function_state&, std::span); + static void consume(function_state&, Input<>, Output<>); }; static_assert(UserDefinedInstruction); struct Returns { - static void execute(std::span, std::span); + static void execute(Input<>, Output); }; static_assert(UserDefinedInstruction); struct ReturnsConsume { - static void consume(std::span, std::span); + static void consume(Input<>, Output); }; static_assert(UserDefinedInstruction); struct FunctionStateReturns { using function_state = int; - static void execute(function_state&, std::span, - std::span); + static void execute(function_state&, Input<>, Output); }; static_assert(UserDefinedInstruction); struct FunctionStateReturnsConsume { using function_state = int; - static void consume(function_state&, std::span, - std::span); + static void consume(function_state&, Input<>, Output); }; static_assert(UserDefinedInstruction); struct ReturnsMultiple { - static void execute(std::span, std::span); + static void execute(Input<>, Output); }; static_assert(UserDefinedInstruction); struct ReturnsMultipleConsume { - static void consume(std::span, std::span); + static void consume(Input<>, Output); }; static_assert(UserDefinedInstruction); struct FunctionStateReturnsMultiple { using function_state = int; - static void execute(function_state&, std::span, - std::span); + static void execute(function_state&, Input<>, Output); }; static_assert(UserDefinedInstruction); struct FunctionStateReturnsMultipleConsume { using function_state = int; - static void consume(function_state&, std::span, - std::span); + static void consume(function_state&, Input<>, Output); }; static_assert(UserDefinedInstruction); -struct FunctionStateReturnsSingletonArray { - using function_state = int; - static std::array execute(function_state&, std::span); -}; -static_assert(not UserDefinedInstruction); - -struct FunctionStateReturnsSingletonArrayConsume { - using function_state = int; - static std::array consume(function_state&, std::span); -}; -static_assert( - not UserDefinedInstruction); - struct ReturnsAndAcceptsValues { - static void execute(std::span, std::span); + static void execute(Input, Output); }; static_assert(UserDefinedInstruction); struct ReturnsAndAcceptsValuesConsume { - static void consume(std::span, std::span); + static void consume(Input, Output); }; static_assert(UserDefinedInstruction); struct FunctionStateReturnsAndAcceptsValues { using function_state = int; - static void execute(function_state&, std::span, - std::span); + static void execute(function_state&, Input, Output); }; static_assert(UserDefinedInstruction); struct FunctionStateReturnsAndAcceptsValuesConsume { using function_state = int; - static void consume(function_state&, std::span, - std::span); + static void consume(function_state&, Input, Output); }; static_assert( UserDefinedInstruction); struct ReturnsAndAcceptsValuesAndImmediates { - static void execute(std::span, std::span, int, char); + static void execute(Input, Output, int, char); }; static_assert(UserDefinedInstruction); struct ReturnsAndAcceptsValuesAndImmediatesConsume { - static void consume(std::span, std::span, int, char); + static void consume(Input, Output, int, char); }; static_assert( UserDefinedInstruction); struct FunctionStateReturnsAndAcceptsValuesAndImmediates { using function_state = int; - static void execute(function_state&, std::span, std::span, - int, char); + static void execute(function_state&, Input, Output, int, + char); }; static_assert( UserDefinedInstruction); struct FunctionStateReturnsAndAcceptsValuesAndImmediatesConsume { using function_state = int; - static void consume(function_state&, std::span, std::span, - int, char); + static void consume(function_state&, Input, Output, int, + char); }; static_assert(UserDefinedInstruction< FunctionStateReturnsAndAcceptsValuesAndImmediatesConsume>); -struct TooBig { - Value v[2]; -}; - -struct ReturnsNotConvertibleAndAcceptsValuesAndImmediates { - static TooBig execute(std::span, int, char); -}; -static_assert(not UserDefinedInstruction< - ReturnsNotConvertibleAndAcceptsValuesAndImmediates>); - -struct ReturnsNotConvertibleAndAcceptsValuesAndImmediatesConsume { - static TooBig consume(std::span, int, char); -}; -static_assert(not UserDefinedInstruction< - ReturnsNotConvertibleAndAcceptsValuesAndImmediatesConsume>); - -struct FunctionStateReturnsNotConvertibleAndAcceptsValuesAndImmediates { - using function_state = int; - static TooBig execute(function_state&, std::span, int, char); -}; -static_assert(not UserDefinedInstruction< - FunctionStateReturnsNotConvertibleAndAcceptsValuesAndImmediates>); - -struct FunctionStateReturnsNotConvertibleAndAcceptsValuesAndImmediatesConsume { - using function_state = int; - static TooBig consume(function_state&, std::span, int, char); -}; -static_assert( - not UserDefinedInstruction< - FunctionStateReturnsNotConvertibleAndAcceptsValuesAndImmediatesConsume>); - struct ExecuteDynamic : Instruction { static void execute(std::span, std::span); }; diff --git a/jasmin/core/metadata_test.cc b/jasmin/core/metadata_test.cc index a3a73b2..c0b996b 100644 --- a/jasmin/core/metadata_test.cc +++ b/jasmin/core/metadata_test.cc @@ -1,15 +1,28 @@ #include "jasmin/core/metadata.h" +#include + #include "nth/test/test.h" namespace jasmin { namespace { +template +struct InputType; + +template +struct InputType> { + template + using JustValue = Value; + using type = Input...>; +}; + template struct Inst : jasmin::Instruction> { - static constexpr void execute(std::span, std::span out, - bool b) { - out[0] = b; + static constexpr void execute( + typename InputType>::type, Output out, + bool b) { + out.set<0>(b); } }; diff --git a/jasmin/core/output.h b/jasmin/core/output.h new file mode 100644 index 0000000..d7812bc --- /dev/null +++ b/jasmin/core/output.h @@ -0,0 +1,37 @@ +#ifndef JASMIN_CORE_OUTPUT_H +#define JASMIN_CORE_OUTPUT_H + +#include "jasmin/core/value.h" +#include "nth/meta/sequence.h" +#include "nth/meta/type.h" + +namespace jasmin { +namespace internal { +struct OutputBase {}; +} // namespace internal + +template +requires((std::constructible_from and ...)) // + struct Output : internal::OutputBase { + private: + static constexpr auto types = nth::type_sequence; + + public: + static constexpr int count = sizeof...(Ts); + + explicit constexpr Output(Value *ptr) : ptr_(ptr) {} + + template + constexpr void set(nth::type_t()> value) { + ptr_[N] = value; + } + + constexpr void set(Ts... ts) { ((*ptr_++ = ts), ...); } + + private: + Value *ptr_; +}; + +} // namespace jasmin + +#endif // JASMIN_CORE_OUTPUT_H diff --git a/jasmin/core/output_test.cc b/jasmin/core/output_test.cc new file mode 100644 index 0000000..9b3dde0 --- /dev/null +++ b/jasmin/core/output_test.cc @@ -0,0 +1,44 @@ +#include "jasmin/core/output.h" + +#include "nth/test/test.h" + +namespace jasmin { +namespace { + +static_assert(Output<>::count == 0); +static_assert(Output::count == 1); +static_assert(Output::count == 2); +static_assert(Output::count == 2); + +NTH_TEST("output/single") { + Value vs[1]; + vs[0] = 3; + + Output out(vs); + + out.set<0>(4); + NTH_EXPECT(vs[0].as() == 4); + + out.set(5); + NTH_EXPECT(vs[0].as() == 5); +} + +NTH_TEST("output/multiple") { + Value vs[2]; + vs[0] = 3; + vs[1] = false; + + Output out(vs); + + out.set<0>(4); + out.set<1>(true); + NTH_EXPECT(vs[0].as() == 4); + NTH_EXPECT(vs[1].as()); + + out.set(5, false); + NTH_EXPECT(vs[0].as() == 5); + NTH_EXPECT(not vs[1].as()); +} + +} // namespace +} // namespace jasmin diff --git a/jasmin/instructions/arithmetic.h b/jasmin/instructions/arithmetic.h index 05d3dda..fd0e239 100644 --- a/jasmin/instructions/arithmetic.h +++ b/jasmin/instructions/arithmetic.h @@ -39,49 +39,43 @@ concept Negatable = not std::same_as and requires(T t) { template struct Add : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = T(values[0].as() + values[1].as()); + static constexpr void consume(Input in, Output out) { + out.template set<0>(in.template get<0>() + in.template get<1>()); } }; template struct Subtract : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = T(values[0].as() - values[1].as()); + static constexpr void consume(Input in, Output out) { + out.template set<0>(in.template get<0>() - in.template get<1>()); } }; template struct Multiply : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = T(values[0].as() * values[1].as()); + static constexpr void consume(Input in, Output out) { + out.template set<0>(in.template get<0>() * in.template get<1>()); } }; template struct Divide : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = T(values[0].as() / values[1].as()); + static constexpr void consume(Input in, Output out) { + out.template set<0>(in.template get<0>() / in.template get<1>()); } }; template struct Mod : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = T(values[0].as() % values[1].as()); + static constexpr void consume(Input in, Output out) { + out.template set<0>(in.template get<0>() % in.template get<1>()); } }; template struct Negate : Instruction> { - static constexpr void execute(std::span values, - std::span) { - values[0] = T(-values[0].as()); + static constexpr void consume(Input in, Output out) { + out.template set<0>(-in.template get<0>()); } }; diff --git a/jasmin/instructions/bool.h b/jasmin/instructions/bool.h index d32195d..725a7d4 100644 --- a/jasmin/instructions/bool.h +++ b/jasmin/instructions/bool.h @@ -6,32 +6,32 @@ namespace jasmin { struct Not : Instruction { - static void execute(std::span values, std::span) { - values[0] = not values[0].as(); + static void consume(Input in, Output out) { + out.set<0>(not in.get<0>()); } }; struct Xor : Instruction { - static void consume(std::span values, std::span out) { - out[0] = static_cast(values[0].as() xor values[1].as()); + static void consume(Input in, Output out) { + out.set<0>(in.get<0>() xor in.get<1>()); } }; struct Or : Instruction { - static void consume(std::span values, std::span out) { - out[0] = values[0].as() or values[1].as(); + static void consume(Input in, Output out) { + out.set<0>(in.get<0>() or in.get<1>()); } }; struct And : Instruction { - static void consume(std::span values, std::span out) { - out[0] = values[0].as() and values[1].as(); + static void consume(Input in, Output out) { + out.set<0>(in.get<0>() and in.get<1>()); } }; struct Nand : Instruction { - static void consume(std::span values, std::span out) { - out[0] = not(values[0].as() and values[1].as()); + static void consume(Input in, Output out) { + out.set<0>(not(in.get<0>() and in.get<1>())); } }; diff --git a/jasmin/instructions/common.h b/jasmin/instructions/common.h index c54458b..b95472e 100644 --- a/jasmin/instructions/common.h +++ b/jasmin/instructions/common.h @@ -9,9 +9,8 @@ namespace jasmin { template struct Push : Instruction> { - static constexpr void execute(std::span, std::span out, - T v) { - out[0] = v; + static constexpr void execute(Input<>, Output out, T v) { + out.template set<0>(v); } static void identify(RegisterCoalescer& p, SsaInstruction const& i) { @@ -20,12 +19,13 @@ struct Push : Instruction> { }; struct Drop : Instruction { - static constexpr void consume(std::span, std::span) {} + static constexpr void consume(Input, Output<>) {} }; struct Swap : Instruction { - static void execute(std::span values, std::span) { - std::swap(values[0], values[1]); + static void consume(Input in, Outputout) { + out.set<0>(in.get<1>()); + out.set<1>(in.get<0>()); } static void identify(RegisterCoalescer& p, SsaInstruction const& i) { @@ -43,8 +43,8 @@ struct Rotate : Instruction { }; struct Duplicate : Instruction { - static void execute(std::span values, std::span out) { - out[0] = values[0]; + static void execute(Input in, Output out) { + out.set<0>(in.get<0>()); } static void identify(RegisterCoalescer& p, SsaInstruction const& i) { @@ -60,16 +60,15 @@ struct DuplicateAt : Instruction { }; struct Load : Instruction { - static void execute(std::span values, std::span, + static void consume(Input in, Output out, size_t size) { - values[0] = Value::Load(values[0].as(), size); + out.set<0>(Value::Load(in.get<0>(), size)); } }; struct Store : Instruction { - static void consume(std::span values, std::span, - uint8_t size) { - Value::Store(values[1], values[0].as(), size); + static void consume(Input in, Output<>, uint8_t size) { + Value::Store(in.get<1>(), in.get<0>(), size); } }; diff --git a/jasmin/instructions/compare.h b/jasmin/instructions/compare.h index a2b837d..2e19807 100644 --- a/jasmin/instructions/compare.h +++ b/jasmin/instructions/compare.h @@ -14,17 +14,15 @@ concept Comparable = requires(T t) { template struct LessThan : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = values[0].as() < values[1].as(); + static constexpr void consume(Input in, Output out) { + out.set<0>(in.template get<0>() < in.template get<1>()); } }; template struct AppendLessThan : Instruction> { - static constexpr void execute(std::span values, - std::span out) { - out[0] = values[0].as() < values[1].as(); + static constexpr void execute(Input in, Output out) { + out.set<0>(in.template get<0>() < in.template get<1>()); } }; @@ -35,17 +33,15 @@ concept Equatable = requires(T t) { template struct Equal : Instruction> { - static constexpr void consume(std::span values, - std::span out) { - out[0] = values[0].as() == values[1].as(); + static constexpr void consume(Input in, Output out) { + out.set<0>(in.template get<0>() == in.template get<1>()); } }; template struct AppendEqual : Instruction> { - static constexpr void execute(std::span values, - std::span out) { - out[0] = values[0].as() == values[1].as(); + static constexpr void execute(Input in, Output out) { + out.set<0>(in.template get<0>() == in.template get<1>()); } }; diff --git a/jasmin/instructions/stack.h b/jasmin/instructions/stack.h index 76fb831..a91dcc3 100644 --- a/jasmin/instructions/stack.h +++ b/jasmin/instructions/stack.h @@ -33,27 +33,19 @@ struct StackFrame { // be called more than once on any `jasmin::Function`. struct StackAllocate : Instruction { using function_state = internal::StackFrame; - static constexpr void execute(function_state &frame, std::span, - std::span, size_t size_in_bytes) { + static constexpr void execute(function_state &frame, Input<>, Output<>, + size_t size_in_bytes) { frame.allocate_once(size_in_bytes); } - static std::string debug(std::span immediates) { - return "stack-allocate" + std::to_string(immediates[0].as()) + - " byte(s)"; - } }; // Returns a pointer into the stack frame associated with the current function, // offset by the amount `offset`. struct StackOffset : Instruction { using function_state = internal::StackFrame; - static constexpr void execute(function_state &frame, std::span, - std::span out, size_t offset) { - out[0] = frame.data() + offset; - } - static std::string debug(std::span immediates) { - return "stack-offset" + std::to_string(immediates[0].as()) + - " byte(s)"; + static constexpr void execute(function_state &frame, Input<>, + Output out, size_t offset) { + out.set<0>(frame.data() + offset); } }; diff --git a/jasmin/testing.h b/jasmin/testing.h index 3be1eb9..adb2900 100644 --- a/jasmin/testing.h +++ b/jasmin/testing.h @@ -2,7 +2,9 @@ #define JASMIN_TESTING_H #include "jasmin/core/function.h" +#include "jasmin/core/input.h" #include "jasmin/core/instruction.h" +#include "jasmin/core/output.h" #include "nth/container/stack.h" namespace jasmin { @@ -26,15 +28,15 @@ requires(internal::HasFunctionState and auto... immediates) { struct WriteFunctionState : Instruction { using function_state = typename Inst::function_state; - static void execute(function_state &state, std::span, - std::span, function_state *in) { + static void execute(function_state &state, Input<>, Output<>, + function_state *in) { state = std::move(*in); } }; struct ExtractFunctionState : Instruction { using function_state = typename Inst::function_state; - static void execute(function_state &state, std::span, - std::span, function_state *out) { + static void execute(function_state &state, Input<>, Output<>, + function_state *out) { *out = std::move(state); } };