@@ -512,31 +512,73 @@ void CodegenLLVMVisitor::visit_boolean(const ast::Boolean& node) {
512512 ir_builder.create_boolean_constant (node.get_value ());
513513}
514514
515- /* *
516- * Currently, this functions is very similar to visiting the binary operator. However, the
517- * difference here is that the writes to the LHS variable must be atomic. These has a particular
518- * use case in synapse kernels. For simplicity, we choose not to support atomic writes at this
519- * stage and emit a warning.
520- *
521- * \todo support this properly.
522- */
523515void CodegenLLVMVisitor::visit_codegen_atomic_statement (const ast::CodegenAtomicStatement& node) {
524- if (platform.is_cpu_with_simd ())
525- logger->warn (" Atomic operations are not supported" );
516+ // Get the variable node that need an atomic update.
517+ const auto & var = std::dynamic_pointer_cast<ast::VarName>(node.get_lhs ());
518+ if (!var)
519+ throw std::runtime_error (" Error: only 'VarName' update is supported\n " );
526520
527- // Support only assignment for now .
521+ // Evaluate RHS of the update .
528522 llvm::Value* rhs = accept_and_get (node.get_rhs ());
529- if (node.get_atomic_op ().get_value () != ast::BinaryOp::BOP_ASSIGN)
530- throw std::runtime_error (
531- " Error: only assignment is supported for CodegenAtomicStatement\n " );
532- const auto & var = dynamic_cast <ast::VarName*>(node.get_lhs ().get ());
533- if (!var)
534- throw std::runtime_error (" Error: only 'VarName' assignment is supported\n " );
535523
536- // Process the assignment as if it was non-atomic.
537- if (platform.is_cpu_with_simd ())
538- logger->warn (" Treating write as non-atomic" );
539- write_to_variable (*var, rhs);
524+ // First, check if it is an atomic write only and we can return early.
525+ // Otherwise, extract what kind of atomic update we want to make.
526+ ast::BinaryOp atomic_op = node.get_atomic_op ().get_value ();
527+ if (atomic_op == ast::BinaryOp::BOP_ASSIGN) {
528+ write_to_variable (*var, rhs);
529+ return ;
530+ }
531+ ast::BinaryOp op = ir_builder.extract_atomic_op (atomic_op);
532+
533+ // For different platforms, we handle atomic updates differently!
534+ if (platform.is_cpu_with_simd ()) {
535+ throw std::runtime_error (" Error: no atomic update support for SIMD CPUs\n " );
536+ } else if (platform.is_gpu ()) {
537+ const auto & identifier = var->get_name ();
538+
539+ // We only need to support atomic updates to instance struct members.
540+ if (!identifier->is_codegen_instance_var ())
541+ throw std::runtime_error (" Error: atomic updates for non-instance variable\n " );
542+
543+ const auto & node = std::dynamic_pointer_cast<ast::CodegenInstanceVar>(identifier);
544+ const auto & instance_name = node->get_instance_var ()->get_node_name ();
545+ const auto & member_node = node->get_member_var ();
546+ const auto & member_name = member_node->get_node_name ();
547+
548+ if (!instance_var_helper.is_an_instance_variable (member_name))
549+ throw std::runtime_error (" Error: " + member_name +
550+ " is not a member of the instance variable\n " );
551+
552+ llvm::Value* instance_ptr = ir_builder.create_load (instance_name);
553+ int member_index = instance_var_helper.get_variable_index (member_name);
554+ llvm::Value* member_ptr = ir_builder.get_struct_member_ptr (instance_ptr, member_index);
555+
556+ // Some sanity checks.
557+ auto codegen_var_with_type = instance_var_helper.get_variable (member_name);
558+ if (!codegen_var_with_type->get_is_pointer ())
559+ throw std::runtime_error (
560+ " Error: atomic updates are allowed on pointer variables only\n " );
561+ const auto & member_var_name = std::dynamic_pointer_cast<ast::VarName>(member_node);
562+ if (!member_var_name->get_name ()->is_indexed_name ())
563+ throw std::runtime_error (" Error: " + member_name + " is not an IndexedName\n " );
564+ const auto & member_indexed_name = std::dynamic_pointer_cast<ast::IndexedName>(
565+ member_var_name->get_name ());
566+ if (!member_indexed_name->get_length ()->is_name ())
567+ throw std::runtime_error (" Error: " + member_name + " must be indexed with a variable!" );
568+
569+ llvm::Value* i64_index = get_index (*member_indexed_name);
570+ llvm::Value* instance_member = ir_builder.create_load (member_ptr);
571+ llvm::Value* ptr = ir_builder.create_inbounds_gep (instance_member, i64_index);
572+
573+ ir_builder.create_atomic_op (ptr, rhs, op);
574+ } else {
575+ // For non-SIMD CPUs, updates don't have to be atomic at all!
576+ llvm::Value* lhs = accept_and_get (node.get_lhs ());
577+ ir_builder.create_binary_op (lhs, rhs, op);
578+ llvm::Value* result = ir_builder.pop_last_value ();
579+
580+ write_to_variable (*var, result);
581+ }
540582}
541583
542584// Generating FOR loop in LLVM IR creates the following structure:
0 commit comments