blob: 3391263afed6e454db2e95998d6e77a819887f65 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- C U D A --
-- --
-- B o d y --
-- --
-- Copyright (C) 2010-2022, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
-- This package defines CUDA-specific datastructures and functions.
with Atree; use Atree;
with Debug; use Debug;
with Einfo.Entities; use Einfo.Entities;
with Einfo.Utils; use Einfo.Utils;
with Elists; use Elists;
with Errout; use Errout;
with Nlists; use Nlists;
with Nmake; use Nmake;
with Sem_Aux; use Sem_Aux;
with Sem_Util; use Sem_Util;
with Sinfo.Nodes; use Sinfo.Nodes;
with Sinfo; use Sinfo;
with GNAT.HTable;
package body GNAT_CUDA is
--------------------------------------
-- Hash Table for CUDA_Global nodes --
--------------------------------------
type Hash_Range is range 0 .. 510;
-- Size of hash table headers
function Hash (F : Entity_Id) return Hash_Range;
-- Hash function for hash table
package CUDA_Device_Entities_Table is new
GNAT.HTable.Simple_HTable
(Header_Num => Hash_Range,
Element => Elist_Id,
No_Element => No_Elist,
Key => Entity_Id,
Hash => Hash,
Equal => "=");
-- The keys of this table are package entities whose bodies contain at
-- least one procedure marked with aspect CUDA_Device. The values are
-- Elists of the marked entities.
package CUDA_Kernels_Table is new
GNAT.HTable.Simple_HTable
(Header_Num => Hash_Range,
Element => Elist_Id,
No_Element => No_Elist,
Key => Entity_Id,
Hash => Hash,
Equal => "=");
-- The keys of this table are package entities whose bodies contain at
-- least one procedure marked with aspect CUDA_Global. The values are
-- Elists of the marked procedures.
procedure Empty_CUDA_Global_Subprograms (Pack_Id : Entity_Id);
-- For all subprograms marked CUDA_Global in Pack_Id, remove declarations
-- and replace statements with a single null statement.
-- This is required because CUDA_Global subprograms could be referring to
-- device-only symbols, which would result in unknown symbols at link time
-- if kept around.
-- We choose to empty CUDA_Global subprograms rather than completely
-- removing them from the package because registering CUDA_Global
-- subprograms with the CUDA runtime on the host requires knowing the
-- subprogram's host-side address.
function Get_CUDA_Device_Entities (Pack_Id : Entity_Id) return Elist_Id;
-- Returns an Elist of all entities marked with pragma CUDA_Device that
-- are declared within package body Pack_Body. Returns No_Elist if Pack_Id
-- does not contain such entities.
procedure Remove_CUDA_Device_Entities (Pack_Id : Entity_Id);
-- Removes all entities marked with the CUDA_Device pragma from package
-- Pack_Id. Must only be called when compiling for the host.
procedure Set_CUDA_Device_Entities
(Pack_Id : Entity_Id;
E : Elist_Id);
-- Stores E as the list of CUDA_Device entities belonging to the package
-- entity Pack_Id. Pack_Id must not have a list of device entities.
procedure Set_CUDA_Kernels
(Pack_Id : Entity_Id;
Kernels : Elist_Id);
-- Stores Kernels as the list of kernels belonging to the package entity
-- Pack_Id. Pack_Id must not have a list of kernels.
----------------------------
-- Add_CUDA_Device_Entity --
----------------------------
procedure Add_CUDA_Device_Entity
(Pack_Id : Entity_Id;
E : Entity_Id)
is
Device_Entities : Elist_Id := Get_CUDA_Device_Entities (Pack_Id);
begin
if No (Device_Entities) then
Device_Entities := New_Elmt_List;
Set_CUDA_Device_Entities (Pack_Id, Device_Entities);
end if;
Append_Elmt (E, Device_Entities);
end Add_CUDA_Device_Entity;
---------------------
-- Add_CUDA_Kernel --
---------------------
procedure Add_CUDA_Kernel
(Pack_Id : Entity_Id;
Kernel : Entity_Id)
is
Kernels : Elist_Id := Get_CUDA_Kernels (Pack_Id);
begin
if No (Kernels) then
Kernels := New_Elmt_List;
Set_CUDA_Kernels (Pack_Id, Kernels);
end if;
Append_Elmt (Kernel, Kernels);
end Add_CUDA_Kernel;
-----------------------------------
-- Empty_CUDA_Global_Subprograms --
-----------------------------------
procedure Empty_CUDA_Global_Subprograms (Pack_Id : Entity_Id) is
Spec_Id : constant Node_Id := Corresponding_Spec (Pack_Id);
Kernels : constant Elist_Id := Get_CUDA_Kernels (Spec_Id);
Kernel_Elm : Elmt_Id;
Kernel : Entity_Id;
Kernel_Body : Node_Id;
Null_Body : Entity_Id;
Loc : Source_Ptr;
begin
-- It is an error to empty CUDA_Global subprograms when not compiling
-- for the host.
pragma Assert (Debug_Flag_Underscore_C);
if No (Kernels) then
return;
end if;
Kernel_Elm := First_Elmt (Kernels);
while Present (Kernel_Elm) loop
Kernel := Node (Kernel_Elm);
Kernel_Body := Subprogram_Body (Kernel);
Loc := Sloc (Kernel_Body);
Null_Body := Make_Subprogram_Body (Loc,
Specification => Specification (Kernel_Body),
Declarations => New_List,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => New_List (Make_Null_Statement (Loc))));
Set_Corresponding_Spec (Null_Body,
Corresponding_Spec (Kernel_Body));
Rewrite (Kernel_Body, Null_Body);
Next_Elmt (Kernel_Elm);
end loop;
end Empty_CUDA_Global_Subprograms;
-------------------------
-- Expand_CUDA_Package --
-------------------------
procedure Expand_CUDA_Package (N : Node_Id) is
begin
-- If not compiling for the host, do not do anything.
if not Debug_Flag_Underscore_C then
return;
end if;
-- Remove the content (both declarations and statements) of CUDA_Global
-- procedures. This is required because CUDA_Global functions could be
-- referencing entities available only on the device, which would result
-- in unknown symbol errors at link time.
Empty_CUDA_Global_Subprograms (N);
-- Remove CUDA_Device entities (except if they are also CUDA_Host), as
-- they can only be referenced from the device and might reference
-- device-only symbols.
Remove_CUDA_Device_Entities
(Package_Specification (Corresponding_Spec (N)));
end Expand_CUDA_Package;
----------
-- Hash --
----------
function Hash (F : Entity_Id) return Hash_Range is
begin
return Hash_Range (F mod 511);
end Hash;
------------------------------
-- Get_CUDA_Device_Entities --
------------------------------
function Get_CUDA_Device_Entities (Pack_Id : Entity_Id) return Elist_Id is
begin
return CUDA_Device_Entities_Table.Get (Pack_Id);
end Get_CUDA_Device_Entities;
----------------------
-- Get_CUDA_Kernels --
----------------------
function Get_CUDA_Kernels (Pack_Id : Entity_Id) return Elist_Id is
begin
return CUDA_Kernels_Table.Get (Pack_Id);
end Get_CUDA_Kernels;
---------------------------------
-- Remove_CUDA_Device_Entities --
---------------------------------
procedure Remove_CUDA_Device_Entities (Pack_Id : Entity_Id) is
Device_Entities : constant Elist_Id :=
Get_CUDA_Device_Entities (Pack_Id);
Device_Elmt : Elmt_Id;
Device_Entity : Entity_Id;
Bod : Node_Id;
begin
pragma Assert (Debug_Flag_Underscore_C);
if No (Device_Entities) then
return;
end if;
Device_Elmt := First_Elmt (Device_Entities);
while Present (Device_Elmt) loop
Device_Entity := Node (Device_Elmt);
Next_Elmt (Device_Elmt);
case Ekind (Device_Entity) is
when E_Function | E_Procedure =>
Bod := Subprogram_Body (Device_Entity);
if Nkind (Parent (Bod)) = N_Subunit
and then Present (Corresponding_Stub (Parent (Bod)))
then
Error_Msg_N
("Cuda_Device not suported on separate subprograms",
Corresponding_Stub (Parent (Bod)));
else
Remove (Bod);
Remove (Subprogram_Spec (Device_Entity));
end if;
when E_Variable | E_Constant =>
Remove (Declaration_Node (Device_Entity));
when others =>
pragma Assert (False);
end case;
Remove_Entity_And_Homonym (Device_Entity);
end loop;
end Remove_CUDA_Device_Entities;
------------------------------
-- Set_CUDA_Device_Entities --
------------------------------
procedure Set_CUDA_Device_Entities
(Pack_Id : Entity_Id;
E : Elist_Id)
is
begin
pragma Assert (No (Get_CUDA_Device_Entities (Pack_Id)));
CUDA_Device_Entities_Table.Set (Pack_Id, E);
end Set_CUDA_Device_Entities;
----------------------
-- Set_CUDA_Kernels --
----------------------
procedure Set_CUDA_Kernels
(Pack_Id : Entity_Id;
Kernels : Elist_Id)
is
begin
pragma Assert (No (Get_CUDA_Kernels (Pack_Id)));
CUDA_Kernels_Table.Set (Pack_Id, Kernels);
end Set_CUDA_Kernels;
end GNAT_CUDA;