Jlm
Loading...
Searching...
No Matches
call.hpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#ifndef JLM_LLVM_IR_OPERATORS_CALL_HPP
7#define JLM_LLVM_IR_OPERATORS_CALL_HPP
8
13#include <jlm/llvm/ir/tac.hpp>
14#include <jlm/llvm/ir/types.hpp>
15#include <jlm/rvsdg/Phi.hpp>
17
18namespace jlm::llvm
19{
20
26{
27public:
28 enum class CallType
29 {
35
41
46
51 };
52
54 : CallType_(callType),
55 Output_(&output)
56 {}
57
61 [[nodiscard]] CallType
62 GetCallType() const noexcept
63 {
64 return CallType_;
65 }
66
71 [[nodiscard]] bool
72 IsNonRecursiveDirectCall() const noexcept
73 {
75 }
76
81 [[nodiscard]] bool
82 IsRecursiveDirectCall() const noexcept
83 {
85 }
86
90 [[nodiscard]] bool
96
101 [[nodiscard]] bool
102 IsExternalCall() const noexcept
103 {
105 }
106
111 [[nodiscard]] bool
112 IsIndirectCall() const noexcept
113 {
115 }
116
124 [[nodiscard]] rvsdg::Output &
125 GetLambdaOutput() const noexcept
126 {
128 {
129 return *Output_;
130 }
131
133 auto argument = jlm::util::assertedCast<jlm::rvsdg::RegionArgument>(Output_);
134 /*
135 * FIXME: This assumes that all recursion variables where added before the dependencies. It
136 * would be better if we did not use the index for retrieving the result, but instead
137 * explicitly encoded it in an phi_argument.
138 */
139 return *argument->region()->result(argument->index())->origin();
140 }
141
148 [[nodiscard]] rvsdg::RegionArgument &
149 GetImport() const noexcept
150 {
152 return *jlm::util::assertedCast<rvsdg::RegionArgument>(Output_);
153 }
154
163 [[nodiscard]] jlm::rvsdg::Output &
164 GetFunctionOrigin() const noexcept
165 {
166 return *Output_;
167 }
168
173 [[nodiscard]] bool
174 isSetjmpCall();
175
180 [[nodiscard]] bool
182
192 static std::unique_ptr<CallTypeClassifier>
194 {
196 return std::make_unique<CallTypeClassifier>(CallType::NonRecursiveDirectCall, output);
197 }
198
208 static std::unique_ptr<CallTypeClassifier>
210 {
211 return std::make_unique<CallTypeClassifier>(CallType::RecursiveDirectCall, output);
212 }
213
223 static std::unique_ptr<CallTypeClassifier>
225 {
226 JLM_ASSERT(argument.region() == &argument.region()->graph()->GetRootRegion());
227 return std::make_unique<CallTypeClassifier>(CallType::ExternalCall, argument);
228 }
229
236 static std::unique_ptr<CallTypeClassifier>
238 {
239 return std::make_unique<CallTypeClassifier>(CallType::IndirectCall, output);
240 }
241
242private:
245};
246
251{
252public:
253 ~CallOperation() override;
254
256 std::shared_ptr<const rvsdg::FunctionType> functionType,
257 CallingConvention callingConvention,
258 AttributeList attributes)
259 : SimpleOperation(create_srctypes(functionType), functionType->Results()),
260 FunctionType_(std::move(functionType)),
261 callingConvention_(callingConvention),
262 attributes_(std::move(attributes))
263 {}
264
265 bool
266 operator==(const Operation & other) const noexcept override;
267
268 [[nodiscard]] std::string
269 debug_string() const override;
270
271 [[nodiscard]] const std::shared_ptr<const rvsdg::FunctionType> &
272 GetFunctionType() const noexcept
273 {
274 return FunctionType_;
275 }
276
277 [[nodiscard]] CallingConvention
278 getCallingConvention() const noexcept
279 {
280 return callingConvention_;
281 }
282
283 [[nodiscard]] const AttributeList &
284 getAttributes() const noexcept
285 {
286 return attributes_;
287 }
288
289 [[nodiscard]] std::unique_ptr<Operation>
290 copy() const override;
291
297 [[nodiscard]] static size_t
298 NumArguments(const rvsdg::Node & node) noexcept
299 {
300 JLM_ASSERT(is<CallOperation>(&node));
301 return node.ninputs() - 1;
302 }
303
309 [[nodiscard]] static rvsdg::Input *
310 Argument(const rvsdg::Node & node, const size_t n)
311 {
312 JLM_ASSERT(is<CallOperation>(&node));
314 return node.input(n + 1);
315 }
316
320 [[nodiscard]] static rvsdg::Input &
321 GetFunctionInput(const rvsdg::Node & node) noexcept
322 {
323 JLM_ASSERT(is<CallOperation>(&node));
324 const auto functionInput = node.input(0);
325 JLM_ASSERT(is<rvsdg::FunctionType>(functionInput->Type()));
326 return *functionInput;
327 }
328
332 [[nodiscard]] static rvsdg::Input &
333 GetIOStateInput(const rvsdg::Node & node) noexcept
334 {
335 JLM_ASSERT(is<CallOperation>(&node));
336 const auto ioState = node.input(node.ninputs() - 2);
337 JLM_ASSERT(is<IOStateType>(ioState->Type()));
338 return *ioState;
339 }
340
344 [[nodiscard]] static rvsdg::Output &
345 GetIOStateOutput(const rvsdg::Node & node) noexcept
346 {
347 JLM_ASSERT(is<CallOperation>(&node));
348 const auto ioState = node.output(node.noutputs() - 2);
349 JLM_ASSERT(is<IOStateType>(ioState->Type()));
350 return *ioState;
351 }
352
353 [[nodiscard]] static rvsdg::Input &
355 {
356 JLM_ASSERT(is<IOStateType>(output.Type()));
357 auto [callNode, callOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(output);
358 JLM_ASSERT(callOperation);
359 return GetIOStateInput(*callNode);
360 }
361
365 [[nodiscard]] static rvsdg::Input &
366 GetMemoryStateInput(const rvsdg::Node & node) noexcept
367 {
368 JLM_ASSERT(is<CallOperation>(&node));
369 const auto memoryState = node.input(node.ninputs() - 1);
370 JLM_ASSERT(is<MemoryStateType>(memoryState->Type()));
371 return *memoryState;
372 }
373
377 [[nodiscard]] static rvsdg::Output &
378 GetMemoryStateOutput(const rvsdg::Node & node) noexcept
379 {
380 JLM_ASSERT(is<CallOperation>(&node));
381 const auto memoryState = node.output(node.noutputs() - 1);
382 JLM_ASSERT(is<MemoryStateType>(memoryState->Type()));
383 return *memoryState;
384 }
385
386 [[nodiscard]] static rvsdg::Input &
388 {
389 JLM_ASSERT(is<MemoryStateType>(output.Type()));
390 auto [callNode, callOperation] = rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(output);
391 JLM_ASSERT(callOperation);
392 return GetMemoryStateInput(*callNode);
393 }
394
405 [[nodiscard]] static rvsdg::SimpleNode *
406 tryGetMemoryStateEntryMerge(const rvsdg::Node & callNode) noexcept
407 {
408 JLM_ASSERT(is<CallOperation>(&callNode));
409 const auto node =
411 return is<CallEntryMemoryStateMergeOperation>(node) ? node : nullptr;
412 }
413
423 [[nodiscard]] static rvsdg::SimpleNode *
424 tryGetMemoryStateExitSplit(const rvsdg::Node & callNode) noexcept
425 {
426 JLM_ASSERT(is<CallOperation>(&callNode));
427
428 // If a memory state exit split node is present, then we would expect the node to be the only
429 // user of the memory state output.
430 if (GetMemoryStateOutput(callNode).nusers() != 1)
431 return nullptr;
432
433 const auto node =
435 return is<CallExitMemoryStateSplitOperation>(node) ? node : nullptr;
436 }
437
449 static rvsdg::Output &
450 TraceFunctionInput(const rvsdg::SimpleNode & callNode);
451
459 static std::unique_ptr<CallTypeClassifier>
460 ClassifyCall(const rvsdg::SimpleNode & callNode);
461
462 static std::unique_ptr<ThreeAddressCode>
464 const Variable * function,
465 std::shared_ptr<const rvsdg::FunctionType> functionType,
466 CallingConvention callingConvention,
467 AttributeList attributes,
468 const std::vector<const Variable *> & arguments)
469 {
470 CheckFunctionInputType(function->type());
471
472 auto op = std::make_unique<CallOperation>(
473 std::move(functionType),
474 callingConvention,
475 std::move(attributes));
476 std::vector<const Variable *> operands({ function });
477 operands.insert(operands.end(), arguments.begin(), arguments.end());
478 return ThreeAddressCode::create(std::move(op), operands);
479 }
480
481 static std::vector<rvsdg::Output *>
483 rvsdg::Output * function,
484 std::shared_ptr<const rvsdg::FunctionType> functionType,
485 const std::vector<rvsdg::Output *> & arguments)
486 {
487 return outputs(&CreateNode(function, std::move(functionType), arguments));
488 }
489
490 static std::vector<rvsdg::Output *>
492 rvsdg::Output * function,
493 std::shared_ptr<const rvsdg::FunctionType> functionType,
494 CallingConvention callingConvention,
495 AttributeList attributes,
496 const std::vector<rvsdg::Output *> & arguments)
497 {
498 return outputs(&CreateNode(
499 function,
500 std::move(functionType),
501 callingConvention,
502 std::move(attributes),
503 arguments));
504 }
505
506 static rvsdg::SimpleNode &
508 rvsdg::Region & region,
509 std::unique_ptr<CallOperation> callOperation,
510 const std::vector<rvsdg::Output *> & operands)
511 {
512 CheckFunctionType(*callOperation->GetFunctionType());
513
514 return rvsdg::SimpleNode::Create(region, std::move(callOperation), operands);
515 }
516
522 static rvsdg::SimpleNode &
524 rvsdg::Output * function,
525 std::shared_ptr<const rvsdg::FunctionType> functionType,
526 const std::vector<rvsdg::Output *> & arguments)
527 {
528 return CreateNode(
529 function,
530 std::move(functionType),
533 arguments);
534 }
535
536 static rvsdg::SimpleNode &
538 rvsdg::Output * function,
539 std::shared_ptr<const rvsdg::FunctionType> functionType,
540 CallingConvention callingConvention,
541 AttributeList attributes,
542 const std::vector<rvsdg::Output *> & arguments)
543 {
544 CheckFunctionInputType(*function->Type());
545
546 auto callOperation = std::make_unique<CallOperation>(
547 std::move(functionType),
548 callingConvention,
549 std::move(attributes));
550 std::vector operands({ function });
551 operands.insert(operands.end(), arguments.begin(), arguments.end());
552
553 return CreateNode(*function->region(), std::move(callOperation), operands);
554 }
555
556private:
557 static inline std::vector<std::shared_ptr<const rvsdg::Type>>
558 create_srctypes(const std::shared_ptr<const rvsdg::FunctionType> & functionType)
559 {
560 std::vector<std::shared_ptr<const rvsdg::Type>> types({ functionType });
561 for (auto & argumentType : functionType->Arguments())
562 types.emplace_back(argumentType);
563
564 return types;
565 }
566
567 static void
569 {
570 if (!is<rvsdg::FunctionType>(type))
571 throw util::Error("Expected function type.");
572 }
573
574 static void
576 {
577 auto CheckArgumentTypes = [](const rvsdg::FunctionType & functionType)
578 {
579 if (functionType.NumArguments() < 2)
580 throw util::Error("Expected at least three argument types.");
581
582 auto memoryStateArgumentIndex = functionType.NumArguments() - 1;
583 auto iOStateArgumentIndex = functionType.NumArguments() - 2;
584
585 if (!is<MemoryStateType>(functionType.ArgumentType(memoryStateArgumentIndex)))
586 throw util::Error("Expected memory state type.");
587
588 if (!is<IOStateType>(functionType.ArgumentType(iOStateArgumentIndex)))
589 throw util::Error("Expected IO state type.");
590 };
591
592 auto CheckResultTypes = [](const rvsdg::FunctionType & functionType)
593 {
594 if (functionType.NumResults() < 2)
595 throw util::Error("Expected at least three result types.");
596
597 auto memoryStateResultIndex = functionType.NumResults() - 1;
598 auto iOStateResultIndex = functionType.NumResults() - 2;
599
600 if (!is<MemoryStateType>(functionType.ResultType(memoryStateResultIndex)))
601 throw util::Error("Expected memory state type.");
602
603 if (!is<IOStateType>(functionType.ResultType(iOStateResultIndex)))
604 throw util::Error("Expected IO state type.");
605 };
606
607 CheckArgumentTypes(functionType);
608 CheckResultTypes(functionType);
609 }
610
611 std::shared_ptr<const rvsdg::FunctionType> FunctionType_;
614};
615
616}
617
618#endif
util::HashSet< rvsdg::Output * > arguments
static AttributeList createEmptyList()
Call operation class.
Definition call.hpp:251
CallingConvention getCallingConvention() const noexcept
Definition call.hpp:278
std::shared_ptr< const rvsdg::FunctionType > FunctionType_
Definition call.hpp:611
static std::vector< rvsdg::Output * > Create(rvsdg::Output *function, std::shared_ptr< const rvsdg::FunctionType > functionType, CallingConvention callingConvention, AttributeList attributes, const std::vector< rvsdg::Output * > &arguments)
Definition call.hpp:491
const AttributeList & getAttributes() const noexcept
Definition call.hpp:284
static std::vector< std::shared_ptr< const rvsdg::Type > > create_srctypes(const std::shared_ptr< const rvsdg::FunctionType > &functionType)
Definition call.hpp:558
static rvsdg::SimpleNode & CreateNode(rvsdg::Output *function, std::shared_ptr< const rvsdg::FunctionType > functionType, const std::vector< rvsdg::Output * > &arguments)
Definition call.hpp:523
static std::unique_ptr< ThreeAddressCode > create(const Variable *function, std::shared_ptr< const rvsdg::FunctionType > functionType, CallingConvention callingConvention, AttributeList attributes, const std::vector< const Variable * > &arguments)
Definition call.hpp:463
std::unique_ptr< Operation > copy() const override
Definition call.cpp:35
static std::unique_ptr< CallTypeClassifier > ClassifyCall(const rvsdg::SimpleNode &callNode)
Classifies a call node.
Definition call.cpp:49
static rvsdg::SimpleNode * tryGetMemoryStateEntryMerge(const rvsdg::Node &callNode) noexcept
Definition call.hpp:406
static rvsdg::Output & GetMemoryStateOutput(const rvsdg::Node &node) noexcept
Definition call.hpp:378
static rvsdg::Input & GetMemoryStateInput(const rvsdg::Node &node) noexcept
Definition call.hpp:366
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< CallOperation > callOperation, const std::vector< rvsdg::Output * > &operands)
Definition call.hpp:507
static rvsdg::Input * Argument(const rvsdg::Node &node, const size_t n)
Definition call.hpp:310
static void CheckFunctionInputType(const jlm::rvsdg::Type &type)
Definition call.hpp:568
static rvsdg::Input & mapMemoryStateOutputToInput(rvsdg::Output &output)
Definition call.hpp:387
static size_t NumArguments(const rvsdg::Node &node) noexcept
Definition call.hpp:298
static rvsdg::Output & TraceFunctionInput(const rvsdg::SimpleNode &callNode)
Traces function input of call node.
Definition call.cpp:41
const std::shared_ptr< const rvsdg::FunctionType > & GetFunctionType() const noexcept
Definition call.hpp:272
static void CheckFunctionType(const rvsdg::FunctionType &functionType)
Definition call.hpp:575
static rvsdg::Input & GetFunctionInput(const rvsdg::Node &node) noexcept
Definition call.hpp:321
bool operator==(const Operation &other) const noexcept override
Definition call.cpp:20
static std::vector< rvsdg::Output * > Create(rvsdg::Output *function, std::shared_ptr< const rvsdg::FunctionType > functionType, const std::vector< rvsdg::Output * > &arguments)
Definition call.hpp:482
static rvsdg::Input & mapIOStateOutputToInput(rvsdg::Output &output)
Definition call.hpp:354
static rvsdg::SimpleNode * tryGetMemoryStateExitSplit(const rvsdg::Node &callNode) noexcept
Definition call.hpp:424
CallOperation(std::shared_ptr< const rvsdg::FunctionType > functionType, CallingConvention callingConvention, AttributeList attributes)
Definition call.hpp:255
std::string debug_string() const override
Definition call.cpp:29
AttributeList attributes_
Definition call.hpp:613
static rvsdg::Input & GetIOStateInput(const rvsdg::Node &node) noexcept
Definition call.hpp:333
CallingConvention callingConvention_
Definition call.hpp:612
static rvsdg::SimpleNode & CreateNode(rvsdg::Output *function, std::shared_ptr< const rvsdg::FunctionType > functionType, CallingConvention callingConvention, AttributeList attributes, const std::vector< rvsdg::Output * > &arguments)
Definition call.hpp:537
static rvsdg::Output & GetIOStateOutput(const rvsdg::Node &node) noexcept
Definition call.hpp:345
Call node classifier.
Definition call.hpp:26
bool IsExternalCall() const noexcept
Determines whether call is an external call.
Definition call.hpp:102
jlm::rvsdg::Output * Output_
Definition call.hpp:244
bool IsIndirectCall() const noexcept
Determines whether call is an indirect call.
Definition call.hpp:112
rvsdg::RegionArgument & GetImport() const noexcept
Returns the imported function.
Definition call.hpp:149
CallTypeClassifier(CallType callType, jlm::rvsdg::Output &output)
Definition call.hpp:53
bool IsNonRecursiveDirectCall() const noexcept
Determines whether call is a non-recursive direct call.
Definition call.hpp:72
CallType GetCallType() const noexcept
Return call type.
Definition call.hpp:62
static std::unique_ptr< CallTypeClassifier > CreateExternalCallClassifier(rvsdg::RegionArgument &argument)
Classify callee as external.
Definition call.hpp:224
jlm::rvsdg::Output & GetFunctionOrigin() const noexcept
Return origin of a call node's function input.
Definition call.hpp:164
static std::unique_ptr< CallTypeClassifier > CreateIndirectCallClassifier(jlm::rvsdg::Output &output)
Classify callee as inderict.
Definition call.hpp:237
static std::unique_ptr< CallTypeClassifier > CreateNonRecursiveDirectCallClassifier(rvsdg::Output &output)
Classify callee as non-recursive.
Definition call.hpp:193
static std::unique_ptr< CallTypeClassifier > CreateRecursiveDirectCallClassifier(rvsdg::Output &output)
Classify callee as recursive.
Definition call.hpp:209
bool IsRecursiveDirectCall() const noexcept
Determines whether call is a recursive direct call.
Definition call.hpp:82
rvsdg::Output & GetLambdaOutput() const noexcept
Returns the called function.
Definition call.hpp:125
bool IsDirectCall() const noexcept
Definition call.hpp:91
static std::unique_ptr< llvm::ThreeAddressCode > create(std::unique_ptr< rvsdg::SimpleOperation > operation, const std::vector< const Variable * > &operands)
Definition tac.hpp:155
const jlm::rvsdg::Type & type() const noexcept
Definition variable.hpp:56
Function type class.
const jlm::rvsdg::Type & ArgumentType(size_t index) const noexcept
size_t NumArguments() const noexcept
size_t NumResults() const noexcept
const jlm::rvsdg::Type & ResultType(size_t index) const noexcept
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
NodeInput * input(size_t index) const noexcept
Definition node.hpp:615
rvsdg::Region * region() const noexcept
Definition node.cpp:151
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
Represents the argument of a region.
Definition region.hpp:41
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
Graph * graph() const noexcept
Definition region.hpp:267
static SimpleNode & Create(Region &region, std::unique_ptr< Operation > operation, const std::vector< rvsdg::Output * > &operands)
SimpleOperation(std::vector< std::shared_ptr< const jlm::rvsdg::Type > > operands, std::vector< std::shared_ptr< const jlm::rvsdg::Type > > results)
Definition operation.hpp:61
#define JLM_ASSERT(x)
Definition common.hpp:16
Global memory state passed between functions.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872