s << ";\n";
}
+/* Construct a function declaration from a call insn. This can be
+ necessary for two reasons - either we have an indirect call which
+ requires a .callprototype declaration, or we have a libcall
+ generated by emit_library_call for which no decl exists. */
+
+static void
+write_func_decl_from_insn (std::stringstream &s, const char *name,
+ rtx result, rtx pat)
+{
+ if (!name)
+ {
+ s << "\t.callprototype ";
+ name = "_";
+ }
+ else
+ {
+ s << "\n// BEGIN GLOBAL FUNCTION DECL: " << name << "\n";
+ s << "\t.extern .func ";
+ }
+
+ if (result != NULL_RTX)
+ s << "(.param"
+ << nvptx_ptx_type_from_mode (arg_promotion (GET_MODE (result)), false)
+ << " %rval) ";
+
+ s << name;
+
+ const char *sep = " (";
+ int arg_end = XVECLEN (pat, 0);
+ for (int i = 1; i < arg_end; i++)
+ {
+ /* We don't have to deal with mode splitting here, as that was
+ already done when generating the call sequence. */
+ machine_mode mode = GET_MODE (XEXP (XVECEXP (pat, 0, i), 0));
+
+ s << sep
+ << ".param"
+ << nvptx_ptx_type_from_mode (mode, false)
+ << " %arg"
+ << i;
+ if (mode == QImode || mode == HImode)
+ s << "[1]";
+ sep = ", ";
+ }
+ if (arg_end != 1)
+ s << ")";
+ s << ";\n";
+}
+
/* Check NAME for special function names and redirect them by returning a
replacement. This applies to malloc, free and realloc, for which we
want to use libgcc wrappers, and call, which triggers a bug in ptxas. */
return name;
}
-/* If DECL is a FUNCTION_DECL, check the hash table to see if we
- already encountered it, and if not, insert it and write a ptx
- declarations that will be output at the end of compilation. */
+/* DECL is an external FUNCTION_DECL, make sure its in the fndecl hash
+ table and and write a ptx prototype. These are emitted at end of
+ compilation. */
-static bool
-nvptx_record_fndecl (tree decl, bool force = false)
+static void
+nvptx_record_fndecl (tree decl)
{
- if (decl == NULL_TREE || TREE_CODE (decl) != FUNCTION_DECL
- || !DECL_EXTERNAL (decl))
- return true;
-
- if (!force && TYPE_ARG_TYPES (TREE_TYPE (decl)) == NULL_TREE)
- return false;
-
tree *slot = declared_fndecls_htab->find_slot (decl, INSERT);
if (*slot == NULL)
{
name = nvptx_name_replacement (name);
write_function_decl_and_comment (func_decls, name, decl);
}
- return true;
}
-/* Record that we need to emit a ptx decl for DECL. Either do it now, or
- record it for later in case we have no argument information at this
- point. */
+/* Record a libcall or unprototyped external function. CALLEE is the
+ SYMBOL_REF. Insert into the libfunc hash table and emit a ptx
+ declaration for it. */
+
+static void
+nvptx_record_libfunc (rtx callee, rtx retval, rtx pat)
+{
+ rtx *slot = declared_libfuncs_htab->find_slot (callee, INSERT);
+ if (*slot == NULL)
+ {
+ *slot = callee;
+
+ const char *name = XSTR (callee, 0);
+ name = nvptx_name_replacement (name);
+ write_func_decl_from_insn (func_decls, name, retval, pat);
+ }
+}
+
+/* DECL is an external FUNCTION_DECL, that we're referencing. If it
+ is prototyped, record it now. Otherwise record it as needed at end
+ of compilation, when we might have more information about it. */
void
nvptx_record_needed_fndecl (tree decl)
{
- if (nvptx_record_fndecl (decl))
- return;
+ if (TYPE_ARG_TYPES (TREE_TYPE (decl)) == NULL_TREE)
+ {
+ tree *slot = needed_fndecls_htab->find_slot (decl, INSERT);
+ if (*slot == NULL)
+ *slot = decl;
+ }
+ else
+ nvptx_record_fndecl (decl);
+}
- tree *slot = needed_fndecls_htab->find_slot (decl, INSERT);
- if (*slot == NULL)
- *slot = decl;
+/* SYM is a SYMBOL_REF. If it refers to an external function, record
+ it as needed. */
+
+static void
+nvptx_maybe_record_fnsym (rtx sym)
+{
+ tree decl = SYMBOL_REF_DECL (sym);
+
+ if (decl && TREE_CODE (decl) == FUNCTION_DECL && DECL_EXTERNAL (decl))
+ nvptx_record_needed_fndecl (decl);
}
/* Emit code to initialize the REGNO predicate register to indicate
return "ret;";
}
-/* Construct a function declaration from a call insn. This can be
- necessary for two reasons - either we have an indirect call which
- requires a .callprototype declaration, or we have a libcall
- generated by emit_library_call for which no decl exists. */
-
-static void
-write_func_decl_from_insn (std::stringstream &s, const char *name,
- rtx result, rtx pat)
-{
- if (!name)
- {
- s << "\t.callprototype ";
- name = "_";
- }
- else
- {
- s << "\n// BEGIN GLOBAL FUNCTION DECL: " << name << "\n";
- s << "\t.extern .func ";
- }
-
- if (result != NULL_RTX)
- s << "(.param"
- << nvptx_ptx_type_from_mode (arg_promotion (GET_MODE (result)), false)
- << " %rval) ";
-
- s << name;
-
- const char *sep = " (";
- int arg_end = XVECLEN (pat, 0);
- for (int i = 1; i < arg_end; i++)
- {
- /* We don't have to deal with mode splitting here, as that was
- already done when generating the call sequence. */
- machine_mode mode = GET_MODE (XEXP (XVECEXP (pat, 0, i), 0));
-
- s << sep
- << ".param"
- << nvptx_ptx_type_from_mode (mode, false)
- << " %arg"
- << i;
- if (mode == QImode || mode == HImode)
- s << "[1]";
- sep = ", ";
- }
- if (arg_end != 1)
- s << ")";
- s << ";\n";
-}
-
/* Terminate a function by writing a closing brace to FILE. */
void
rtx callee = XEXP (address, 0);
rtx pat, t;
rtvec vec;
- bool external_decl = false;
rtx varargs = NULL_RTX;
- tree decl_type = NULL_TREE;
unsigned parallel = 0;
for (t = cfun->machine->call_args; t; t = XEXP (t, 1))
tree decl = SYMBOL_REF_DECL (callee);
if (decl != NULL_TREE)
{
- decl_type = TREE_TYPE (decl);
if (DECL_STATIC_CHAIN (decl))
cfun->machine->has_call_with_sc = true;
- if (DECL_EXTERNAL (decl))
- external_decl = true;
+
tree attr = get_oacc_fn_attrib (decl);
if (attr)
{
gcc_assert (vec_pos = XVECLEN (pat, 0));
- /* If this is a libcall, decl_type is NULL. For a call to a non-libcall
- undeclared function, we'll have an external decl without arg types.
- In either case we have to try to construct a ptx declaration from one of
- the calls to the function. */
- if (!REG_P (callee)
- && (decl_type == NULL_TREE
- || (external_decl && TYPE_ARG_TYPES (decl_type) == NULL_TREE)))
- {
- rtx *slot = declared_libfuncs_htab->find_slot (callee, INSERT);
- if (*slot == NULL)
- {
- *slot = callee;
-
- const char *name = XSTR (callee, 0);
- if (decl_type)
- name = nvptx_name_replacement (name);
- write_func_decl_from_insn (func_decls, name, retval, pat);
- }
- }
-
nvptx_emit_forking (parallel, true);
emit_call_insn (pat);
nvptx_emit_joining (parallel, true);
}
/* When loading an operand ORIG_OP, verify whether an address space
- conversion to generic is required, and if so, perform it. Also
- check for SYMBOL_REFs for function decls and call
- nvptx_record_needed_fndecl as needed.
- Return either the original operand, or the converted one. */
+ conversion to generic is required, and if so, perform it. Check
+ for SYMBOL_REFs and record them if needed. Return either the
+ original operand, or the converted one. */
rtx
nvptx_maybe_convert_symbolic_operand (rtx orig_op)
if (GET_CODE (op) != SYMBOL_REF)
return orig_op;
- tree decl = SYMBOL_REF_DECL (op);
- if (decl && TREE_CODE (decl) == FUNCTION_DECL)
- {
- nvptx_record_needed_fndecl (decl);
- return orig_op;
- }
-
+ nvptx_maybe_record_fnsym (op);
+
addr_space_t as = nvptx_addr_space_from_address (op);
if (as == ADDR_SPACE_GENERIC)
return orig_op;
static bool
nvptx_assemble_integer (rtx x, unsigned int size, int ARG_UNUSED (aligned_p))
{
- if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST)
+ HOST_WIDE_INT val = 0;
+
+ switch (GET_CODE (x))
{
+ default:
+ gcc_unreachable ();
+
+ case CONST_INT:
+ val = INTVAL (x);
+ nvptx_assemble_value (val, size);
+ break;
+
+ case CONST:
+ x = XEXP (x, 0);
+ gcc_assert (GET_CODE (x) == PLUS);
+ val = INTVAL (XEXP (x, 1));
+ x = XEXP (x, 0);
+ gcc_assert (GET_CODE (x) == SYMBOL_REF);
+ /* FALLTHROUGH */
+
+ case SYMBOL_REF:
gcc_assert (size = decl_chunk_size);
if (decl_offset % decl_chunk_size != 0)
sorry ("cannot emit unaligned pointers in ptx assembly");
decl_offset += size;
begin_decl_field ();
- HOST_WIDE_INT off = 0;
- if (GET_CODE (x) == CONST)
- x = XEXP (x, 0);
- if (GET_CODE (x) == PLUS)
- {
- off = INTVAL (XEXP (x, 1));
- x = XEXP (x, 0);
- }
- if (GET_CODE (x) == SYMBOL_REF)
- {
- nvptx_record_needed_fndecl (SYMBOL_REF_DECL (x));
- fprintf (asm_out_file, "generic(");
- output_address (VOIDmode, x);
- fprintf (asm_out_file, ")");
- }
- if (off != 0)
- fprintf (asm_out_file, " + " HOST_WIDE_INT_PRINT_DEC, off);
- return true;
- }
+ nvptx_maybe_record_fnsym (x);
+ fprintf (asm_out_file, "generic(");
+ output_address (VOIDmode, x);
+ fprintf (asm_out_file, ")");
- HOST_WIDE_INT val;
- switch (GET_CODE (x))
- {
- case CONST_INT:
- val = INTVAL (x);
- break;
- case CONST_DOUBLE:
- gcc_unreachable ();
+ if (val)
+ fprintf (asm_out_file, " + " HOST_WIDE_INT_PRINT_DEC, val);
break;
- default:
- gcc_unreachable ();
}
- nvptx_assemble_value (val, size);
return true;
}
if (GET_CODE (callee) == SYMBOL_REF)
{
decl = SYMBOL_REF_DECL (callee);
- if (decl && DECL_EXTERNAL (decl))
+ if (!decl
+ || (DECL_EXTERNAL (decl) && !TYPE_ARG_TYPES (TREE_TYPE (decl))))
+ nvptx_record_libfunc (callee, result, pat);
+ else if (DECL_EXTERNAL (decl))
nvptx_record_fndecl (decl);
}
hash_table<tree_hasher>::iterator iter;
tree decl;
FOR_EACH_HASH_TABLE_ELEMENT (*needed_fndecls_htab, decl, tree, iter)
- nvptx_record_fndecl (decl, true);
+ nvptx_record_fndecl (decl);
fputs (func_decls.str().c_str(), asm_out_file);
if (worker_bcast_size)