mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 20:01:22 -05:00
gccrs: Add base for HIR to GCC GENERIC lowering
This pass walks the HIR crate and turns them into GCC `tree`s. We do not have any Rust specific tree's. We are slowly removing the backend abstraction which was ported over from gccgo in favour of using `tree`s directly. gcc/rust/ * backend/rust-builtins.h: New. * backend/rust-compile-base.cc: New. * backend/rust-compile-base.h: New. * backend/rust-mangle.cc: New. * backend/rust-mangle.h: New. * backend/rust-tree.cc: New. * backend/rust-tree.h: New. * rust-backend.h: New. * rust-gcc.cc: New. Co-authored-by: David Faust <david.faust@oracle.com>
This commit is contained in:
committed by
Arthur Cohen
parent
509e4c32c6
commit
15f04af347
189
gcc/rust/backend/rust-builtins.h
Normal file
189
gcc/rust/backend/rust-builtins.h
Normal file
@@ -0,0 +1,189 @@
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef RUST_BUILTINS_H
|
||||
#define RUST_BUILTINS_H
|
||||
|
||||
#include "rust-system.h"
|
||||
#include "tree.h"
|
||||
#include "langhooks.h"
|
||||
|
||||
namespace Rust {
|
||||
namespace Compile {
|
||||
|
||||
// https://github.com/rust-lang/rust/blob/master/library/core/src/intrinsics.rs
|
||||
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs
|
||||
// https://github.com/Rust-GCC/gccrs/issues/658
|
||||
//
|
||||
// let llvm_name = match name {
|
||||
// sym::sqrtf32 => "llvm.sqrt.f32",
|
||||
// sym::sqrtf64 => "llvm.sqrt.f64",
|
||||
// sym::powif32 => "llvm.powi.f32",
|
||||
// sym::powif64 => "llvm.powi.f64",
|
||||
// sym::sinf32 => "llvm.sin.f32",
|
||||
// sym::sinf64 => "llvm.sin.f64",
|
||||
// sym::cosf32 => "llvm.cos.f32",
|
||||
// sym::cosf64 => "llvm.cos.f64",
|
||||
// sym::powf32 => "llvm.pow.f32",
|
||||
// sym::powf64 => "llvm.pow.f64",
|
||||
// sym::expf32 => "llvm.exp.f32",
|
||||
// sym::expf64 => "llvm.exp.f64",
|
||||
// sym::exp2f32 => "llvm.exp2.f32",
|
||||
// sym::exp2f64 => "llvm.exp2.f64",
|
||||
// sym::logf32 => "llvm.log.f32",
|
||||
// sym::logf64 => "llvm.log.f64",
|
||||
// sym::log10f32 => "llvm.log10.f32",
|
||||
// sym::log10f64 => "llvm.log10.f64",
|
||||
// sym::log2f32 => "llvm.log2.f32",
|
||||
// sym::log2f64 => "llvm.log2.f64",
|
||||
// sym::fmaf32 => "llvm.fma.f32",
|
||||
// sym::fmaf64 => "llvm.fma.f64",
|
||||
// sym::fabsf32 => "llvm.fabs.f32",
|
||||
// sym::fabsf64 => "llvm.fabs.f64",
|
||||
// sym::minnumf32 => "llvm.minnum.f32",
|
||||
// sym::minnumf64 => "llvm.minnum.f64",
|
||||
// sym::maxnumf32 => "llvm.maxnum.f32",
|
||||
// sym::maxnumf64 => "llvm.maxnum.f64",
|
||||
// sym::copysignf32 => "llvm.copysign.f32",
|
||||
// sym::copysignf64 => "llvm.copysign.f64",
|
||||
// sym::floorf32 => "llvm.floor.f32",
|
||||
// sym::floorf64 => "llvm.floor.f64",
|
||||
// sym::ceilf32 => "llvm.ceil.f32",
|
||||
// sym::ceilf64 => "llvm.ceil.f64",
|
||||
// sym::truncf32 => "llvm.trunc.f32",
|
||||
// sym::truncf64 => "llvm.trunc.f64",
|
||||
// sym::rintf32 => "llvm.rint.f32",
|
||||
// sym::rintf64 => "llvm.rint.f64",
|
||||
// sym::nearbyintf32 => "llvm.nearbyint.f32",
|
||||
// sym::nearbyintf64 => "llvm.nearbyint.f64",
|
||||
// sym::roundf32 => "llvm.round.f32",
|
||||
// sym::roundf64 => "llvm.round.f64",
|
||||
// _ => return None,
|
||||
// };
|
||||
// Some(cx.get_intrinsic(&llvm_name))
|
||||
class BuiltinsContext
|
||||
{
|
||||
public:
|
||||
static BuiltinsContext &get ()
|
||||
{
|
||||
static BuiltinsContext instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool lookup_simple_builtin (const std::string &name, tree *builtin)
|
||||
{
|
||||
auto it = rust_intrinsic_to_gcc_builtin.find (name);
|
||||
if (it == rust_intrinsic_to_gcc_builtin.end ())
|
||||
return false;
|
||||
|
||||
return lookup_gcc_builtin (it->second, builtin);
|
||||
}
|
||||
|
||||
private:
|
||||
static const int builtin_const = 1 << 0;
|
||||
static const int builtin_noreturn = 1 << 1;
|
||||
static const int builtin_novops = 1 << 2;
|
||||
|
||||
BuiltinsContext () { setup (); }
|
||||
|
||||
void setup ()
|
||||
{
|
||||
tree math_function_type_f32
|
||||
= build_function_type_list (float_type_node, float_type_node, NULL_TREE);
|
||||
|
||||
define_builtin ("sinf32", BUILT_IN_SINF, "__builtin_sinf", "sinf",
|
||||
math_function_type_f32, builtin_const);
|
||||
|
||||
define_builtin ("sqrtf32", BUILT_IN_SQRTF, "__builtin_sqrtf", "sqrtf",
|
||||
math_function_type_f32, builtin_const);
|
||||
|
||||
define_builtin ("unreachable", BUILT_IN_UNREACHABLE,
|
||||
"__builtin_unreachable", NULL,
|
||||
build_function_type (void_type_node, void_list_node),
|
||||
builtin_const | builtin_noreturn);
|
||||
|
||||
define_builtin ("abort", BUILT_IN_ABORT, "__builtin_abort", "abort",
|
||||
build_function_type (void_type_node, void_list_node),
|
||||
builtin_const | builtin_noreturn);
|
||||
|
||||
define_builtin ("breakpoint", BUILT_IN_TRAP, "__builtin_trap", "breakpoint",
|
||||
build_function_type (void_type_node, void_list_node),
|
||||
builtin_const | builtin_noreturn);
|
||||
|
||||
define_builtin (
|
||||
"memcpy", BUILT_IN_MEMCPY, "__builtin_memcpy", "memcpy",
|
||||
build_function_type_list (build_pointer_type (void_type_node),
|
||||
build_pointer_type (void_type_node),
|
||||
build_pointer_type (void_type_node),
|
||||
size_type_node, NULL_TREE),
|
||||
0);
|
||||
}
|
||||
|
||||
// Define a builtin function. BCODE is the builtin function code
|
||||
// defined by builtins.def. NAME is the name of the builtin function.
|
||||
// LIBNAME is the name of the corresponding library function, and is
|
||||
// NULL if there isn't one. FNTYPE is the type of the function.
|
||||
// CONST_P is true if the function has the const attribute.
|
||||
// NORETURN_P is true if the function has the noreturn attribute.
|
||||
void define_builtin (const std::string rust_name, built_in_function bcode,
|
||||
const char *name, const char *libname, tree fntype,
|
||||
int flags)
|
||||
{
|
||||
tree decl = add_builtin_function (name, fntype, bcode, BUILT_IN_NORMAL,
|
||||
libname, NULL_TREE);
|
||||
if ((flags & builtin_const) != 0)
|
||||
TREE_READONLY (decl) = 1;
|
||||
if ((flags & builtin_noreturn) != 0)
|
||||
TREE_THIS_VOLATILE (decl) = 1;
|
||||
if ((flags & builtin_novops) != 0)
|
||||
DECL_IS_NOVOPS (decl) = 1;
|
||||
set_builtin_decl (bcode, decl, true);
|
||||
this->builtin_functions_[name] = decl;
|
||||
if (libname != NULL)
|
||||
{
|
||||
decl = add_builtin_function (libname, fntype, bcode, BUILT_IN_NORMAL,
|
||||
NULL, NULL_TREE);
|
||||
if ((flags & builtin_const) != 0)
|
||||
TREE_READONLY (decl) = 1;
|
||||
if ((flags & builtin_noreturn) != 0)
|
||||
TREE_THIS_VOLATILE (decl) = 1;
|
||||
if ((flags & builtin_novops) != 0)
|
||||
DECL_IS_NOVOPS (decl) = 1;
|
||||
this->builtin_functions_[libname] = decl;
|
||||
}
|
||||
|
||||
rust_intrinsic_to_gcc_builtin[rust_name] = name;
|
||||
}
|
||||
|
||||
bool lookup_gcc_builtin (const std::string &name, tree *builtin)
|
||||
{
|
||||
auto it = builtin_functions_.find (name);
|
||||
if (it == builtin_functions_.end ())
|
||||
return false;
|
||||
|
||||
*builtin = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// A mapping of the GCC built-ins exposed to GCC Rust.
|
||||
std::map<std::string, tree> builtin_functions_;
|
||||
std::map<std::string, std::string> rust_intrinsic_to_gcc_builtin;
|
||||
};
|
||||
|
||||
} // namespace Compile
|
||||
} // namespace Rust
|
||||
|
||||
#endif // RUST_BUILTINS_H
|
||||
730
gcc/rust/backend/rust-compile-base.cc
Normal file
730
gcc/rust/backend/rust-compile-base.cc
Normal file
@@ -0,0 +1,730 @@
|
||||
// Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
||||
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "rust-compile-base.h"
|
||||
#include "rust-abi.h"
|
||||
#include "rust-compile-item.h"
|
||||
#include "rust-compile-stmt.h"
|
||||
#include "rust-compile-expr.h"
|
||||
#include "rust-compile-fnparam.h"
|
||||
#include "rust-compile-var-decl.h"
|
||||
#include "rust-constexpr.h"
|
||||
#include "rust-diagnostics.h"
|
||||
#include "rust-expr.h" // for AST::AttrInputLiteral
|
||||
#include "rust-macro.h" // for AST::MetaNameValueStr
|
||||
|
||||
#include "fold-const.h"
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "tree.h"
|
||||
|
||||
namespace Rust {
|
||||
namespace Compile {
|
||||
|
||||
bool inline should_mangle_item (const tree fndecl)
|
||||
{
|
||||
return lookup_attribute ("no_mangle", DECL_ATTRIBUTES (fndecl)) == NULL_TREE;
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::setup_fndecl (tree fndecl, bool is_main_entry_point,
|
||||
bool is_generic_fn, HIR::Visibility &visibility,
|
||||
const HIR::FunctionQualifiers &qualifiers,
|
||||
const AST::AttrVec &attrs)
|
||||
{
|
||||
// if its the main fn or pub visibility mark its as DECL_PUBLIC
|
||||
// please see https://github.com/Rust-GCC/gccrs/pull/137
|
||||
bool is_pub = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
|
||||
if (is_main_entry_point || (is_pub && !is_generic_fn))
|
||||
{
|
||||
TREE_PUBLIC (fndecl) = 1;
|
||||
}
|
||||
|
||||
// is it a const fn
|
||||
if (qualifiers.is_const ())
|
||||
{
|
||||
TREE_READONLY (fndecl) = 1;
|
||||
}
|
||||
|
||||
// is it inline?
|
||||
for (const auto &attr : attrs)
|
||||
{
|
||||
bool is_inline = attr.get_path ().as_string ().compare ("inline") == 0;
|
||||
bool is_must_use
|
||||
= attr.get_path ().as_string ().compare ("must_use") == 0;
|
||||
bool is_cold = attr.get_path ().as_string ().compare ("cold") == 0;
|
||||
bool is_link_section
|
||||
= attr.get_path ().as_string ().compare ("link_section") == 0;
|
||||
bool no_mangle = attr.get_path ().as_string ().compare ("no_mangle") == 0;
|
||||
bool is_deprecated
|
||||
= attr.get_path ().as_string ().compare ("deprecated") == 0;
|
||||
|
||||
if (is_inline)
|
||||
{
|
||||
handle_inline_attribute_on_fndecl (fndecl, attr);
|
||||
}
|
||||
else if (is_must_use)
|
||||
{
|
||||
handle_must_use_attribute_on_fndecl (fndecl, attr);
|
||||
}
|
||||
else if (is_cold)
|
||||
{
|
||||
handle_cold_attribute_on_fndecl (fndecl, attr);
|
||||
}
|
||||
else if (is_link_section)
|
||||
{
|
||||
handle_link_section_attribute_on_fndecl (fndecl, attr);
|
||||
}
|
||||
else if (is_deprecated)
|
||||
{
|
||||
handle_deprecated_attribute_on_fndecl (fndecl, attr);
|
||||
}
|
||||
else if (no_mangle)
|
||||
{
|
||||
handle_no_mangle_attribute_on_fndecl (fndecl, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::handle_cold_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr)
|
||||
{
|
||||
// simple #[cold]
|
||||
if (!attr.has_attr_input ())
|
||||
{
|
||||
tree cold = get_identifier ("cold");
|
||||
// this will get handled by the GCC backend later
|
||||
DECL_ATTRIBUTES (fndecl)
|
||||
= tree_cons (cold, NULL_TREE, DECL_ATTRIBUTES (fndecl));
|
||||
return;
|
||||
}
|
||||
|
||||
rust_error_at (attr.get_locus (),
|
||||
"attribute %<cold%> does not accept any arguments");
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::handle_link_section_attribute_on_fndecl (
|
||||
tree fndecl, const AST::Attribute &attr)
|
||||
{
|
||||
if (!attr.has_attr_input ())
|
||||
{
|
||||
rust_error_at (attr.get_locus (),
|
||||
"%<link_section%> expects exactly one argment");
|
||||
return;
|
||||
}
|
||||
|
||||
rust_assert (attr.get_attr_input ().get_attr_input_type ()
|
||||
== AST::AttrInput::AttrInputType::LITERAL);
|
||||
|
||||
auto &literal = static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
|
||||
const auto &msg_str = literal.get_literal ().as_string ();
|
||||
|
||||
if (decl_section_name (fndecl))
|
||||
{
|
||||
rust_warning_at (attr.get_locus (), 0, "section name redefined");
|
||||
}
|
||||
|
||||
set_decl_section_name (fndecl, msg_str.c_str ());
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::handle_no_mangle_attribute_on_fndecl (
|
||||
tree fndecl, const AST::Attribute &attr)
|
||||
{
|
||||
if (attr.has_attr_input ())
|
||||
{
|
||||
rust_error_at (attr.get_locus (),
|
||||
"attribute %<no_mangle%> does not accept any arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("no_mangle"), NULL_TREE,
|
||||
DECL_ATTRIBUTES (fndecl));
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::handle_deprecated_attribute_on_fndecl (
|
||||
tree fndecl, const AST::Attribute &attr)
|
||||
{
|
||||
tree value = NULL_TREE;
|
||||
TREE_DEPRECATED (fndecl) = 1;
|
||||
|
||||
// simple #[deprecated]
|
||||
if (!attr.has_attr_input ())
|
||||
return;
|
||||
|
||||
const AST::AttrInput &input = attr.get_attr_input ();
|
||||
auto input_type = input.get_attr_input_type ();
|
||||
|
||||
if (input_type == AST::AttrInput::AttrInputType::LITERAL)
|
||||
{
|
||||
// handle #[deprecated = "message"]
|
||||
auto &literal
|
||||
= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
|
||||
const auto &msg_str = literal.get_literal ().as_string ();
|
||||
value = build_string (msg_str.size (), msg_str.c_str ());
|
||||
}
|
||||
else if (input_type == AST::AttrInput::AttrInputType::TOKEN_TREE)
|
||||
{
|
||||
// handle #[deprecated(since = "...", note = "...")]
|
||||
const auto &option = static_cast<const AST::DelimTokenTree &> (input);
|
||||
AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
|
||||
for (const auto &item : meta_item->get_items ())
|
||||
{
|
||||
auto converted_item = item->to_meta_name_value_str ();
|
||||
if (!converted_item)
|
||||
continue;
|
||||
auto key_value = converted_item->get_name_value_pair ();
|
||||
if (key_value.first.compare ("since") == 0)
|
||||
{
|
||||
// valid, but this is handled by Cargo and some third-party audit
|
||||
// tools
|
||||
continue;
|
||||
}
|
||||
else if (key_value.first.compare ("note") == 0)
|
||||
{
|
||||
const auto &msg_str = key_value.second;
|
||||
if (value)
|
||||
rust_error_at (attr.get_locus (), "multiple %<note%> items");
|
||||
value = build_string (msg_str.size (), msg_str.c_str ());
|
||||
}
|
||||
else
|
||||
{
|
||||
rust_error_at (attr.get_locus (), "unknown meta item %qs",
|
||||
key_value.first.c_str ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
tree attr_list = build_tree_list (NULL_TREE, value);
|
||||
DECL_ATTRIBUTES (fndecl)
|
||||
= tree_cons (get_identifier ("deprecated"), attr_list,
|
||||
DECL_ATTRIBUTES (fndecl));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::handle_inline_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr)
|
||||
{
|
||||
// simple #[inline]
|
||||
if (!attr.has_attr_input ())
|
||||
{
|
||||
DECL_DECLARED_INLINE_P (fndecl) = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const AST::AttrInput &input = attr.get_attr_input ();
|
||||
bool is_token_tree
|
||||
= input.get_attr_input_type () == AST::AttrInput::AttrInputType::TOKEN_TREE;
|
||||
rust_assert (is_token_tree);
|
||||
const auto &option = static_cast<const AST::DelimTokenTree &> (input);
|
||||
AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
|
||||
if (meta_item->get_items ().size () != 1)
|
||||
{
|
||||
rust_error_at (attr.get_locus (), "invalid number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string inline_option
|
||||
= meta_item->get_items ().at (0)->as_string ();
|
||||
|
||||
// we only care about NEVER and ALWAYS else its an error
|
||||
bool is_always = inline_option.compare ("always") == 0;
|
||||
bool is_never = inline_option.compare ("never") == 0;
|
||||
|
||||
// #[inline(never)]
|
||||
if (is_never)
|
||||
{
|
||||
DECL_UNINLINABLE (fndecl) = 1;
|
||||
}
|
||||
// #[inline(always)]
|
||||
else if (is_always)
|
||||
{
|
||||
DECL_DECLARED_INLINE_P (fndecl) = 1;
|
||||
DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
|
||||
NULL, DECL_ATTRIBUTES (fndecl));
|
||||
}
|
||||
else
|
||||
{
|
||||
rust_error_at (attr.get_locus (), "unknown inline option");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::handle_must_use_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr)
|
||||
{
|
||||
tree nodiscard = get_identifier ("nodiscard");
|
||||
tree value = NULL_TREE;
|
||||
|
||||
if (attr.has_attr_input ())
|
||||
{
|
||||
rust_assert (attr.get_attr_input ().get_attr_input_type ()
|
||||
== AST::AttrInput::AttrInputType::LITERAL);
|
||||
|
||||
auto &literal
|
||||
= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
|
||||
const auto &msg_str = literal.get_literal ().as_string ();
|
||||
tree message = build_string (msg_str.size (), msg_str.c_str ());
|
||||
|
||||
value = tree_cons (nodiscard, message, NULL_TREE);
|
||||
}
|
||||
|
||||
DECL_ATTRIBUTES (fndecl)
|
||||
= tree_cons (nodiscard, value, DECL_ATTRIBUTES (fndecl));
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::setup_abi_options (tree fndecl, ABI abi)
|
||||
{
|
||||
tree abi_tree = NULL_TREE;
|
||||
|
||||
switch (abi)
|
||||
{
|
||||
case Rust::ABI::RUST:
|
||||
case Rust::ABI::INTRINSIC:
|
||||
case Rust::ABI::C:
|
||||
case Rust::ABI::CDECL:
|
||||
// `decl_attributes` function (not the macro) has the side-effect of
|
||||
// actually switching the codegen backend to use the ABI we annotated.
|
||||
// However, since `cdecl` is the default ABI GCC will be using, explicitly
|
||||
// specifying that ABI will cause GCC to emit a warning saying the
|
||||
// attribute is useless (which is confusing to the user as the attribute
|
||||
// is added by us).
|
||||
DECL_ATTRIBUTES (fndecl)
|
||||
= tree_cons (get_identifier ("cdecl"), NULL, DECL_ATTRIBUTES (fndecl));
|
||||
|
||||
return;
|
||||
|
||||
case Rust::ABI::STDCALL:
|
||||
abi_tree = get_identifier ("stdcall");
|
||||
|
||||
break;
|
||||
|
||||
case Rust::ABI::FASTCALL:
|
||||
abi_tree = get_identifier ("fastcall");
|
||||
|
||||
break;
|
||||
|
||||
case Rust::ABI::SYSV64:
|
||||
abi_tree = get_identifier ("sysv_abi");
|
||||
|
||||
break;
|
||||
|
||||
case Rust::ABI::WIN64:
|
||||
abi_tree = get_identifier ("ms_abi");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
decl_attributes (&fndecl, build_tree_list (abi_tree, NULL_TREE), 0);
|
||||
}
|
||||
|
||||
// ported from gcc/c/c-typecheck.c
|
||||
//
|
||||
// Mark EXP saying that we need to be able to take the
|
||||
// address of it; it should not be allocated in a register.
|
||||
// Returns true if successful. ARRAY_REF_P is true if this
|
||||
// is for ARRAY_REF construction - in that case we don't want
|
||||
// to look through VIEW_CONVERT_EXPR from VECTOR_TYPE to ARRAY_TYPE,
|
||||
// it is fine to use ARRAY_REFs for vector subscripts on vector
|
||||
// register variables.
|
||||
bool
|
||||
HIRCompileBase::mark_addressable (tree exp, Location locus)
|
||||
{
|
||||
tree x = exp;
|
||||
|
||||
while (1)
|
||||
switch (TREE_CODE (x))
|
||||
{
|
||||
case VIEW_CONVERT_EXPR:
|
||||
if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
|
||||
&& VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (x, 0))))
|
||||
return true;
|
||||
x = TREE_OPERAND (x, 0);
|
||||
break;
|
||||
|
||||
case COMPONENT_REF:
|
||||
// TODO
|
||||
// if (DECL_C_BIT_FIELD (TREE_OPERAND (x, 1)))
|
||||
// {
|
||||
// error ("cannot take address of bit-field %qD", TREE_OPERAND (x,
|
||||
// 1)); return false;
|
||||
// }
|
||||
|
||||
/* FALLTHRU */
|
||||
case ADDR_EXPR:
|
||||
case ARRAY_REF:
|
||||
case REALPART_EXPR:
|
||||
case IMAGPART_EXPR:
|
||||
x = TREE_OPERAND (x, 0);
|
||||
break;
|
||||
|
||||
case COMPOUND_LITERAL_EXPR:
|
||||
TREE_ADDRESSABLE (x) = 1;
|
||||
TREE_ADDRESSABLE (COMPOUND_LITERAL_EXPR_DECL (x)) = 1;
|
||||
return true;
|
||||
|
||||
case CONSTRUCTOR:
|
||||
TREE_ADDRESSABLE (x) = 1;
|
||||
return true;
|
||||
|
||||
case VAR_DECL:
|
||||
case CONST_DECL:
|
||||
case PARM_DECL:
|
||||
case RESULT_DECL:
|
||||
// (we don't have a concept of a "register" declaration)
|
||||
// fallthrough */
|
||||
|
||||
/* FALLTHRU */
|
||||
case FUNCTION_DECL:
|
||||
TREE_ADDRESSABLE (x) = 1;
|
||||
|
||||
/* FALLTHRU */
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tree
|
||||
HIRCompileBase::address_expression (tree expr, Location location)
|
||||
{
|
||||
if (expr == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
if (!mark_addressable (expr, location))
|
||||
return error_mark_node;
|
||||
|
||||
return build_fold_addr_expr_loc (location.gcc_location (), expr);
|
||||
}
|
||||
|
||||
tree
|
||||
HIRCompileBase::indirect_expression (tree expr, Location locus)
|
||||
{
|
||||
if (expr == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
return build_fold_indirect_ref_loc (locus.gcc_location (), expr);
|
||||
}
|
||||
|
||||
std::vector<Bvariable *>
|
||||
HIRCompileBase::compile_locals_for_block (Context *ctx, Resolver::Rib &rib,
|
||||
tree fndecl)
|
||||
{
|
||||
std::vector<Bvariable *> locals;
|
||||
for (auto it : rib.get_declarations ())
|
||||
{
|
||||
NodeId node_id = it.first;
|
||||
HirId ref = UNKNOWN_HIRID;
|
||||
if (!ctx->get_mappings ()->lookup_node_to_hir (node_id, &ref))
|
||||
continue;
|
||||
|
||||
// we only care about local patterns
|
||||
HIR::Pattern *pattern = ctx->get_mappings ()->lookup_hir_pattern (ref);
|
||||
if (pattern == nullptr)
|
||||
continue;
|
||||
|
||||
// lookup the type
|
||||
TyTy::BaseType *tyty = nullptr;
|
||||
if (!ctx->get_tyctx ()->lookup_type (ref, &tyty))
|
||||
continue;
|
||||
|
||||
// compile the local
|
||||
tree type = TyTyResolveCompile::compile (ctx, tyty);
|
||||
Bvariable *compiled
|
||||
= CompileVarDecl::compile (fndecl, type, pattern, ctx);
|
||||
locals.push_back (compiled);
|
||||
}
|
||||
return locals;
|
||||
}
|
||||
|
||||
void
|
||||
HIRCompileBase::compile_function_body (Context *ctx, tree fndecl,
|
||||
HIR::BlockExpr &function_body,
|
||||
bool has_return_type)
|
||||
{
|
||||
for (auto &s : function_body.get_statements ())
|
||||
{
|
||||
auto compiled_expr = CompileStmt::Compile (s.get (), ctx);
|
||||
if (compiled_expr != nullptr)
|
||||
{
|
||||
tree s = convert_to_void (compiled_expr, ICV_STATEMENT);
|
||||
ctx->add_statement (s);
|
||||
}
|
||||
}
|
||||
|
||||
if (function_body.has_expr ())
|
||||
{
|
||||
// the previous passes will ensure this is a valid return
|
||||
// or a valid trailing expression
|
||||
tree compiled_expr
|
||||
= CompileExpr::Compile (function_body.expr.get (), ctx);
|
||||
|
||||
if (compiled_expr != nullptr)
|
||||
{
|
||||
if (has_return_type)
|
||||
{
|
||||
std::vector<tree> retstmts;
|
||||
retstmts.push_back (compiled_expr);
|
||||
|
||||
auto ret = ctx->get_backend ()->return_statement (
|
||||
fndecl, retstmts,
|
||||
function_body.get_final_expr ()->get_locus ());
|
||||
ctx->add_statement (ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME can this actually happen?
|
||||
ctx->add_statement (compiled_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tree
|
||||
HIRCompileBase::compile_function (
|
||||
Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
|
||||
std::vector<HIR::FunctionParam> &function_params,
|
||||
const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
|
||||
AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
|
||||
const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
|
||||
bool function_has_return)
|
||||
{
|
||||
tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
|
||||
std::string ir_symbol_name
|
||||
= canonical_path->get () + fntype->subst_as_string ();
|
||||
|
||||
// we don't mangle the main fn since we haven't implemented the main shim
|
||||
bool is_main_fn = fn_name.compare ("main") == 0;
|
||||
std::string asm_name = fn_name;
|
||||
|
||||
unsigned int flags = 0;
|
||||
tree fndecl = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name,
|
||||
"" /* asm_name */, flags, locus);
|
||||
|
||||
setup_fndecl (fndecl, is_main_fn, fntype->has_subsititions_defined (),
|
||||
visibility, qualifiers, outer_attrs);
|
||||
setup_abi_options (fndecl, qualifiers.get_abi ());
|
||||
|
||||
// conditionally mangle the function name
|
||||
bool should_mangle = should_mangle_item (fndecl);
|
||||
if (!is_main_fn && should_mangle)
|
||||
asm_name = ctx->mangle_item (fntype, *canonical_path);
|
||||
SET_DECL_ASSEMBLER_NAME (fndecl,
|
||||
get_identifier_with_length (asm_name.data (),
|
||||
asm_name.length ()));
|
||||
|
||||
// insert into the context
|
||||
ctx->insert_function_decl (fntype, fndecl);
|
||||
|
||||
// setup the params
|
||||
TyTy::BaseType *tyret = fntype->get_return_type ();
|
||||
std::vector<Bvariable *> param_vars;
|
||||
if (!self_param.is_error ())
|
||||
{
|
||||
rust_assert (fntype->is_method ());
|
||||
TyTy::BaseType *self_tyty_lookup = fntype->get_self_type ();
|
||||
|
||||
tree self_type = TyTyResolveCompile::compile (ctx, self_tyty_lookup);
|
||||
Bvariable *compiled_self_param
|
||||
= CompileSelfParam::compile (ctx, fndecl, self_param, self_type,
|
||||
self_param.get_locus ());
|
||||
|
||||
param_vars.push_back (compiled_self_param);
|
||||
ctx->insert_var_decl (self_param.get_mappings ().get_hirid (),
|
||||
compiled_self_param);
|
||||
}
|
||||
|
||||
// offset from + 1 for the TyTy::FnType being used when this is a method to
|
||||
// skip over Self on the FnType
|
||||
bool is_method = !self_param.is_error ();
|
||||
size_t i = is_method ? 1 : 0;
|
||||
for (auto &referenced_param : function_params)
|
||||
{
|
||||
auto tyty_param = fntype->param_at (i++);
|
||||
auto param_tyty = tyty_param.second;
|
||||
auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
|
||||
|
||||
Location param_locus = referenced_param.get_locus ();
|
||||
Bvariable *compiled_param_var
|
||||
= CompileFnParam::compile (ctx, fndecl, &referenced_param,
|
||||
compiled_param_type, param_locus);
|
||||
|
||||
param_vars.push_back (compiled_param_var);
|
||||
|
||||
const HIR::Pattern ¶m_pattern = *referenced_param.get_param_name ();
|
||||
ctx->insert_var_decl (param_pattern.get_pattern_mappings ().get_hirid (),
|
||||
compiled_param_var);
|
||||
}
|
||||
|
||||
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
|
||||
return error_mark_node;
|
||||
|
||||
// lookup locals
|
||||
auto body_mappings = function_body->get_mappings ();
|
||||
Resolver::Rib *rib = nullptr;
|
||||
bool ok
|
||||
= ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib);
|
||||
rust_assert (ok);
|
||||
|
||||
std::vector<Bvariable *> locals
|
||||
= compile_locals_for_block (ctx, *rib, fndecl);
|
||||
|
||||
tree enclosing_scope = NULL_TREE;
|
||||
Location start_location = function_body->get_locus ();
|
||||
Location end_location = function_body->get_end_locus ();
|
||||
|
||||
tree code_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
|
||||
start_location, end_location);
|
||||
ctx->push_block (code_block);
|
||||
|
||||
Bvariable *return_address = nullptr;
|
||||
if (function_has_return)
|
||||
{
|
||||
tree return_type = TyTyResolveCompile::compile (ctx, tyret);
|
||||
|
||||
bool address_is_taken = false;
|
||||
tree ret_var_stmt = NULL_TREE;
|
||||
|
||||
return_address
|
||||
= ctx->get_backend ()->temporary_variable (fndecl, code_block,
|
||||
return_type, NULL,
|
||||
address_is_taken, locus,
|
||||
&ret_var_stmt);
|
||||
|
||||
ctx->add_statement (ret_var_stmt);
|
||||
}
|
||||
|
||||
ctx->push_fn (fndecl, return_address);
|
||||
compile_function_body (ctx, fndecl, *function_body, function_has_return);
|
||||
tree bind_tree = ctx->pop_block ();
|
||||
|
||||
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
|
||||
DECL_SAVED_TREE (fndecl) = bind_tree;
|
||||
|
||||
ctx->pop_fn ();
|
||||
ctx->push_function (fndecl);
|
||||
|
||||
return fndecl;
|
||||
}
|
||||
|
||||
tree
|
||||
HIRCompileBase::compile_constant_item (
|
||||
Context *ctx, TyTy::BaseType *resolved_type,
|
||||
const Resolver::CanonicalPath *canonical_path, HIR::Expr *const_value_expr,
|
||||
Location locus)
|
||||
{
|
||||
const std::string &ident = canonical_path->get ();
|
||||
tree type = TyTyResolveCompile::compile (ctx, resolved_type);
|
||||
tree const_type = build_qualified_type (type, TYPE_QUAL_CONST);
|
||||
|
||||
bool is_block_expr
|
||||
= const_value_expr->get_expression_type () == HIR::Expr::ExprType::Block;
|
||||
|
||||
// compile the expression
|
||||
tree folded_expr = error_mark_node;
|
||||
if (!is_block_expr)
|
||||
{
|
||||
tree value = CompileExpr::Compile (const_value_expr, ctx);
|
||||
folded_expr = fold_expr (value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in order to compile a block expr we want to reuse as much existing
|
||||
// machineary that we already have. This means the best approach is to
|
||||
// make a _fake_ function with a block so it can hold onto temps then
|
||||
// use our constexpr code to fold it completely or error_mark_node
|
||||
Backend::typed_identifier receiver;
|
||||
tree compiled_fn_type = ctx->get_backend ()->function_type (
|
||||
receiver, {}, {Backend::typed_identifier ("_", const_type, locus)},
|
||||
NULL, locus);
|
||||
|
||||
tree fndecl
|
||||
= ctx->get_backend ()->function (compiled_fn_type, ident, "", 0, locus);
|
||||
TREE_READONLY (fndecl) = 1;
|
||||
|
||||
tree enclosing_scope = NULL_TREE;
|
||||
HIR::BlockExpr *function_body
|
||||
= static_cast<HIR::BlockExpr *> (const_value_expr);
|
||||
Location start_location = function_body->get_locus ();
|
||||
Location end_location = function_body->get_end_locus ();
|
||||
|
||||
tree code_block
|
||||
= ctx->get_backend ()->block (fndecl, enclosing_scope, {},
|
||||
start_location, end_location);
|
||||
ctx->push_block (code_block);
|
||||
|
||||
bool address_is_taken = false;
|
||||
tree ret_var_stmt = NULL_TREE;
|
||||
Bvariable *return_address
|
||||
= ctx->get_backend ()->temporary_variable (fndecl, code_block,
|
||||
const_type, NULL,
|
||||
address_is_taken, locus,
|
||||
&ret_var_stmt);
|
||||
|
||||
ctx->add_statement (ret_var_stmt);
|
||||
ctx->push_fn (fndecl, return_address);
|
||||
|
||||
compile_function_body (ctx, fndecl, *function_body, true);
|
||||
tree bind_tree = ctx->pop_block ();
|
||||
|
||||
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
|
||||
DECL_SAVED_TREE (fndecl) = bind_tree;
|
||||
|
||||
ctx->pop_fn ();
|
||||
|
||||
// lets fold it into a call expr
|
||||
tree call = build_call_array_loc (locus.gcc_location (), const_type,
|
||||
fndecl, 0, NULL);
|
||||
folded_expr = fold_expr (call);
|
||||
}
|
||||
|
||||
return named_constant_expression (const_type, ident, folded_expr, locus);
|
||||
}
|
||||
|
||||
tree
|
||||
HIRCompileBase::named_constant_expression (tree type_tree,
|
||||
const std::string &name,
|
||||
tree const_val, Location location)
|
||||
{
|
||||
if (type_tree == error_mark_node || const_val == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
tree name_tree = get_identifier_with_length (name.data (), name.length ());
|
||||
tree decl
|
||||
= build_decl (location.gcc_location (), CONST_DECL, name_tree, type_tree);
|
||||
DECL_INITIAL (decl) = const_val;
|
||||
TREE_CONSTANT (decl) = 1;
|
||||
TREE_READONLY (decl) = 1;
|
||||
|
||||
rust_preserve_from_gc (decl);
|
||||
return decl;
|
||||
}
|
||||
|
||||
} // namespace Compile
|
||||
} // namespace Rust
|
||||
146
gcc/rust/backend/rust-compile-base.h
Normal file
146
gcc/rust/backend/rust-compile-base.h
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
||||
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef RUST_COMPILE_BASE
|
||||
#define RUST_COMPILE_BASE
|
||||
|
||||
#include "rust-compile-context.h"
|
||||
#include "rust-compile-type.h"
|
||||
#include "rust-hir-visitor.h"
|
||||
#include "rust-hir-full.h"
|
||||
|
||||
namespace Rust {
|
||||
namespace Compile {
|
||||
|
||||
class HIRCompileBase
|
||||
{
|
||||
public:
|
||||
virtual ~HIRCompileBase () {}
|
||||
|
||||
protected:
|
||||
HIRCompileBase (Context *ctx) : ctx (ctx) {}
|
||||
|
||||
Context *ctx;
|
||||
|
||||
protected:
|
||||
Context *get_context () { return ctx; }
|
||||
|
||||
tree coercion_site (HirId id, tree rvalue, const TyTy::BaseType *actual,
|
||||
const TyTy::BaseType *expected, Location lvalue_locus,
|
||||
Location rvalue_locus);
|
||||
tree coercion_site1 (tree rvalue, const TyTy::BaseType *actual,
|
||||
const TyTy::BaseType *expected, Location lvalue_locus,
|
||||
Location rvalue_locus);
|
||||
|
||||
tree coerce_to_dyn_object (tree compiled_ref, const TyTy::BaseType *actual,
|
||||
const TyTy::DynamicObjectType *ty, Location locus);
|
||||
|
||||
tree compute_address_for_trait_item (
|
||||
const Resolver::TraitItemReference *ref,
|
||||
const TyTy::TypeBoundPredicate *predicate,
|
||||
std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
|
||||
&receiver_bounds,
|
||||
const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus);
|
||||
|
||||
bool verify_array_capacities (tree ltype, tree rtype, Location ltype_locus,
|
||||
Location rtype_locus);
|
||||
|
||||
tree query_compile (HirId ref, TyTy::BaseType *lookup,
|
||||
const HIR::PathIdentSegment &final_segment,
|
||||
const Analysis::NodeMapping &mappings,
|
||||
Location expr_locus, bool is_qualified_path);
|
||||
|
||||
tree resolve_adjustements (std::vector<Resolver::Adjustment> &adjustments,
|
||||
tree expression, Location locus);
|
||||
|
||||
tree resolve_deref_adjustment (Resolver::Adjustment &adjustment,
|
||||
tree expression, Location locus);
|
||||
|
||||
tree resolve_indirection_adjustment (Resolver::Adjustment &adjustment,
|
||||
tree expression, Location locus);
|
||||
|
||||
tree resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
|
||||
tree expression, Location locus);
|
||||
|
||||
tree resolve_unsized_slice_adjustment (Resolver::Adjustment &adjustment,
|
||||
tree expression, Location locus);
|
||||
|
||||
tree resolve_unsized_dyn_adjustment (Resolver::Adjustment &adjustment,
|
||||
tree expression, Location locus);
|
||||
|
||||
static void setup_fndecl (tree fndecl, bool is_main_entry_point,
|
||||
bool is_generic_fn, HIR::Visibility &visibility,
|
||||
const HIR::FunctionQualifiers &qualifiers,
|
||||
const AST::AttrVec &attrs);
|
||||
|
||||
static void handle_inline_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr);
|
||||
|
||||
static void handle_cold_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr);
|
||||
|
||||
static void handle_must_use_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr);
|
||||
|
||||
static void
|
||||
handle_link_section_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr);
|
||||
static void
|
||||
handle_deprecated_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr);
|
||||
|
||||
static void handle_no_mangle_attribute_on_fndecl (tree fndecl,
|
||||
const AST::Attribute &attr);
|
||||
|
||||
static void setup_abi_options (tree fndecl, ABI abi);
|
||||
|
||||
static tree address_expression (tree expr, Location locus);
|
||||
|
||||
static tree indirect_expression (tree expr, Location locus);
|
||||
|
||||
static bool mark_addressable (tree, Location);
|
||||
|
||||
static std::vector<Bvariable *>
|
||||
compile_locals_for_block (Context *ctx, Resolver::Rib &rib, tree fndecl);
|
||||
|
||||
static void compile_function_body (Context *ctx, tree fndecl,
|
||||
HIR::BlockExpr &function_body,
|
||||
bool has_return_type);
|
||||
|
||||
static tree compile_function (
|
||||
Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
|
||||
std::vector<HIR::FunctionParam> &function_params,
|
||||
const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
|
||||
AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
|
||||
const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
|
||||
bool function_has_return);
|
||||
|
||||
static tree
|
||||
compile_constant_item (Context *ctx, TyTy::BaseType *resolved_type,
|
||||
const Resolver::CanonicalPath *canonical_path,
|
||||
HIR::Expr *const_value_expr, Location locus);
|
||||
|
||||
static tree named_constant_expression (tree type_tree,
|
||||
const std::string &name,
|
||||
tree const_val, Location location);
|
||||
};
|
||||
|
||||
} // namespace Compile
|
||||
} // namespace Rust
|
||||
|
||||
#endif // RUST_COMPILE_BASE
|
||||
307
gcc/rust/backend/rust-mangle.cc
Normal file
307
gcc/rust/backend/rust-mangle.cc
Normal file
@@ -0,0 +1,307 @@
|
||||
#include "rust-mangle.h"
|
||||
#include "fnv-hash.h"
|
||||
#include "rust-base62.h"
|
||||
|
||||
// FIXME: Rename those to legacy_*
|
||||
static const std::string kMangledSymbolPrefix = "_ZN";
|
||||
static const std::string kMangledSymbolDelim = "E";
|
||||
static const std::string kMangledGenericDelim = "$C$";
|
||||
static const std::string kMangledSubstBegin = "$LT$";
|
||||
static const std::string kMangledSubstEnd = "$GT$";
|
||||
static const std::string kMangledSpace = "$u20$";
|
||||
static const std::string kMangledRef = "$RF$";
|
||||
static const std::string kMangledPtr = "$BP$";
|
||||
static const std::string kMangledLeftSqParen = "$u5b$"; // [
|
||||
static const std::string kMangledRightSqParen = "$u5d$"; // ]
|
||||
static const std::string kQualPathBegin = "_" + kMangledSubstBegin;
|
||||
static const std::string kMangledComma = "$C$";
|
||||
|
||||
namespace Rust {
|
||||
namespace Compile {
|
||||
|
||||
Mangler::MangleVersion Mangler::version = MangleVersion::LEGACY;
|
||||
|
||||
static std::string
|
||||
legacy_mangle_name (const std::string &name)
|
||||
{
|
||||
// example
|
||||
// <&T as core::fmt::Debug>::fmt:
|
||||
// _ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h6dac924c0051eef7E
|
||||
// replace all white space with $ and & with RF
|
||||
//
|
||||
// <example::Bar as example::A>::fooA:
|
||||
// _ZN43_$LT$example..Bar$u20$as$u20$example..A$GT$4fooA17hfc615fa76c7db7a0E:
|
||||
//
|
||||
// core::ptr::const_ptr::<impl *const T>::cast:
|
||||
// _ZN4core3ptr9const_ptr33_$LT$impl$u20$$BP$const$u20$T$GT$4cast17hb79f4617226f1d55E:
|
||||
//
|
||||
// core::ptr::const_ptr::<impl *const [T]>::as_ptr:
|
||||
// _ZN4core3ptr9const_ptr43_$LT$impl$u20$$BP$const$u20$$u5b$T$u5d$$GT$6as_ptr17he16e0dcd9473b04fE:
|
||||
//
|
||||
// example::Foo<T>::new:
|
||||
// _ZN7example12Foo$LT$T$GT$3new17h9a2aacb7fd783515E:
|
||||
//
|
||||
// <example::Identity as example::FnLike<&T,&T>>::call
|
||||
// _ZN74_$LT$example..Identity$u20$as$u20$example..FnLike$LT$$RF$T$C$$RF$T$GT$$GT$4call17ha9ee58935895acb3E
|
||||
|
||||
std::string buffer;
|
||||
for (size_t i = 0; i < name.size (); i++)
|
||||
{
|
||||
std::string m;
|
||||
char c = name.at (i);
|
||||
|
||||
if (c == ' ')
|
||||
m = kMangledSpace;
|
||||
else if (c == '&')
|
||||
m = kMangledRef;
|
||||
else if (i == 0 && c == '<')
|
||||
m = kQualPathBegin;
|
||||
else if (c == '<')
|
||||
m = kMangledSubstBegin;
|
||||
else if (c == '>')
|
||||
m = kMangledSubstEnd;
|
||||
else if (c == '*')
|
||||
m = kMangledPtr;
|
||||
else if (c == '[')
|
||||
m = kMangledLeftSqParen;
|
||||
else if (c == ']')
|
||||
m = kMangledRightSqParen;
|
||||
else if (c == ',')
|
||||
m = kMangledComma;
|
||||
else if (c == ':')
|
||||
{
|
||||
rust_assert (i + 1 < name.size ());
|
||||
rust_assert (name.at (i + 1) == ':');
|
||||
i++;
|
||||
m = "..";
|
||||
}
|
||||
else
|
||||
m.push_back (c);
|
||||
|
||||
buffer += m;
|
||||
}
|
||||
|
||||
return std::to_string (buffer.size ()) + buffer;
|
||||
}
|
||||
|
||||
static std::string
|
||||
legacy_mangle_canonical_path (const Resolver::CanonicalPath &path)
|
||||
{
|
||||
std::string buffer;
|
||||
for (size_t i = 0; i < path.size (); i++)
|
||||
{
|
||||
auto &seg = path.get_seg_at (i);
|
||||
buffer += legacy_mangle_name (seg.second);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// rustc uses a sip128 hash for legacy mangling, but an fnv 128 was quicker to
|
||||
// implement for now
|
||||
static std::string
|
||||
legacy_hash (const std::string &fingerprint)
|
||||
{
|
||||
Hash::FNV128 hasher;
|
||||
hasher.write ((const unsigned char *) fingerprint.c_str (),
|
||||
fingerprint.size ());
|
||||
|
||||
uint64_t hi, lo;
|
||||
hasher.sum (&hi, &lo);
|
||||
|
||||
char hex[16 + 1];
|
||||
memset (hex, 0, sizeof hex);
|
||||
snprintf (hex, sizeof hex, "%08" PRIx64 "%08" PRIx64, lo, hi);
|
||||
|
||||
return "h" + std::string (hex, sizeof (hex) - 1);
|
||||
}
|
||||
|
||||
static std::string
|
||||
v0_tuple_prefix (const TyTy::BaseType *ty)
|
||||
{
|
||||
if (ty->is_unit ())
|
||||
return "u";
|
||||
|
||||
// FIXME: ARTHUR: Add rest of algorithm
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string
|
||||
v0_numeric_prefix (const TyTy::BaseType *ty)
|
||||
{
|
||||
static const std::map<std::string, std::string> num_prefixes = {
|
||||
{"[i8]", "a"}, {"[u8]", "h"}, {"[i16]", "s"}, {"[u16]", "t"},
|
||||
{"[i32]", "l"}, {"[u32]", "m"}, {"[i64]", "x"}, {"[u64]", "y"},
|
||||
{"[isize]", "i"}, {"[usize]", "j"}, {"[f32]", "f"}, {"[f64]", "d"},
|
||||
};
|
||||
|
||||
auto ty_kind = ty->get_kind ();
|
||||
auto ty_str = ty->as_string ();
|
||||
auto numeric_iter = num_prefixes.end ();
|
||||
|
||||
// Special numeric types
|
||||
if (ty_kind == TyTy::TypeKind::ISIZE)
|
||||
return "i";
|
||||
else if (ty_kind == TyTy::TypeKind::USIZE)
|
||||
return "j";
|
||||
|
||||
numeric_iter = num_prefixes.find (ty_str);
|
||||
if (numeric_iter != num_prefixes.end ())
|
||||
return numeric_iter->second;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string
|
||||
v0_simple_type_prefix (const TyTy::BaseType *ty)
|
||||
{
|
||||
switch (ty->get_kind ())
|
||||
{
|
||||
case TyTy::TypeKind::BOOL:
|
||||
return "b";
|
||||
case TyTy::TypeKind::CHAR:
|
||||
return "c";
|
||||
case TyTy::TypeKind::STR:
|
||||
return "e";
|
||||
case TyTy::TypeKind::NEVER:
|
||||
return "z";
|
||||
|
||||
// Placeholder types
|
||||
case TyTy::TypeKind::ERROR: // Fallthrough
|
||||
case TyTy::TypeKind::INFER: // Fallthrough
|
||||
case TyTy::TypeKind::PLACEHOLDER: // Fallthrough
|
||||
case TyTy::TypeKind::PARAM:
|
||||
// FIXME: TyTy::TypeKind::BOUND is also a valid variant in rustc
|
||||
return "p";
|
||||
|
||||
case TyTy::TypeKind::TUPLE:
|
||||
return v0_tuple_prefix (ty);
|
||||
|
||||
case TyTy::TypeKind::UINT: // Fallthrough
|
||||
case TyTy::TypeKind::INT: // Fallthrough
|
||||
case TyTy::TypeKind::FLOAT: // Fallthrough
|
||||
case TyTy::TypeKind::ISIZE: // Fallthrough
|
||||
case TyTy::TypeKind::USIZE: // Fallthrough
|
||||
return v0_numeric_prefix (ty);
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
// Add an underscore-terminated base62 integer to the mangling string.
|
||||
// This corresponds to the `<base-62-number>` grammar in the v0 mangling RFC:
|
||||
// - 0 is encoded as "_"
|
||||
// - any other value is encoded as itself minus one in base 62, followed by
|
||||
// "_"
|
||||
static void
|
||||
v0_add_integer_62 (std::string &mangled, uint64_t x)
|
||||
{
|
||||
if (x > 0)
|
||||
mangled.append (base62_integer (x - 1));
|
||||
|
||||
mangled.append ("_");
|
||||
}
|
||||
|
||||
// Add a tag-prefixed base62 integer to the mangling string when the
|
||||
// integer is greater than 0:
|
||||
// - 0 is encoded as "" (nothing)
|
||||
// - any other value is encoded as <tag> + v0_add_integer_62(itself), that is
|
||||
// <tag> + base62(itself - 1) + '_'
|
||||
static void
|
||||
v0_add_opt_integer_62 (std::string &mangled, std::string tag, uint64_t x)
|
||||
{
|
||||
if (x > 0)
|
||||
{
|
||||
mangled.append (tag);
|
||||
v0_add_integer_62 (mangled, x);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
v0_add_disambiguator (std::string &mangled, uint64_t dis)
|
||||
{
|
||||
v0_add_opt_integer_62 (mangled, "s", dis);
|
||||
}
|
||||
|
||||
// Add an identifier to the mangled string. This corresponds to the
|
||||
// `<identifier>` grammar in the v0 mangling RFC.
|
||||
static void
|
||||
v0_add_identifier (std::string &mangled, const std::string &identifier)
|
||||
{
|
||||
// FIXME: gccrs cannot handle unicode identifiers yet, so we never have to
|
||||
// create mangling for unicode values for now. However, this is handled
|
||||
// by the v0 mangling scheme. The grammar for unicode identifier is
|
||||
// contained in <undisambiguated-identifier>, right under the <identifier>
|
||||
// one. If the identifier contains unicode values, then an extra "u" needs
|
||||
// to be added to the mangling string and `punycode` must be used to encode
|
||||
// the characters.
|
||||
|
||||
mangled += std::to_string (identifier.size ());
|
||||
|
||||
// If the first character of the identifier is a digit or an underscore, we
|
||||
// add an extra underscore
|
||||
if (identifier[0] == '_')
|
||||
mangled.append ("_");
|
||||
|
||||
mangled.append (identifier);
|
||||
}
|
||||
|
||||
static std::string
|
||||
v0_type_prefix (const TyTy::BaseType *ty)
|
||||
{
|
||||
auto ty_prefix = v0_simple_type_prefix (ty);
|
||||
if (!ty_prefix.empty ())
|
||||
return ty_prefix;
|
||||
|
||||
// FIXME: We need to fetch more type prefixes
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
static std::string
|
||||
legacy_mangle_item (const TyTy::BaseType *ty,
|
||||
const Resolver::CanonicalPath &path)
|
||||
{
|
||||
const std::string hash = legacy_hash (ty->as_string ());
|
||||
const std::string hash_sig = legacy_mangle_name (hash);
|
||||
|
||||
return kMangledSymbolPrefix + legacy_mangle_canonical_path (path) + hash_sig
|
||||
+ kMangledSymbolDelim;
|
||||
}
|
||||
|
||||
static std::string
|
||||
v0_mangle_item (const TyTy::BaseType *ty, const Resolver::CanonicalPath &path)
|
||||
{
|
||||
// we can get this from the canonical_path
|
||||
auto mappings = Analysis::Mappings::get ();
|
||||
std::string crate_name;
|
||||
bool ok = mappings->get_crate_name (path.get_crate_num (), crate_name);
|
||||
rust_assert (ok);
|
||||
|
||||
std::string mangled;
|
||||
// FIXME: Add real algorithm once all pieces are implemented
|
||||
auto ty_prefix = v0_type_prefix (ty);
|
||||
v0_add_identifier (mangled, crate_name);
|
||||
v0_add_disambiguator (mangled, 62);
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
std::string
|
||||
Mangler::mangle_item (const TyTy::BaseType *ty,
|
||||
const Resolver::CanonicalPath &path) const
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case Mangler::MangleVersion::LEGACY:
|
||||
return legacy_mangle_item (ty, path);
|
||||
case Mangler::MangleVersion::V0:
|
||||
return v0_mangle_item (ty, path);
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Compile
|
||||
} // namespace Rust
|
||||
52
gcc/rust/backend/rust-mangle.h
Normal file
52
gcc/rust/backend/rust-mangle.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef RUST_MANGLE_H
|
||||
#define RUST_MANGLE_H
|
||||
|
||||
#include "rust-system.h"
|
||||
#include "rust-tyty.h"
|
||||
|
||||
namespace Rust {
|
||||
namespace Compile {
|
||||
|
||||
class Mangler
|
||||
{
|
||||
public:
|
||||
enum MangleVersion
|
||||
{
|
||||
// Values defined in rust/lang.opt
|
||||
LEGACY = 0,
|
||||
V0 = 1,
|
||||
};
|
||||
|
||||
// this needs to support Legacy and V0 see github #429 or #305
|
||||
std::string mangle_item (const TyTy::BaseType *ty,
|
||||
const Resolver::CanonicalPath &path) const;
|
||||
|
||||
static void set_mangling (int frust_mangling_value)
|
||||
{
|
||||
version = static_cast<MangleVersion> (frust_mangling_value);
|
||||
}
|
||||
|
||||
private:
|
||||
static enum MangleVersion version;
|
||||
};
|
||||
|
||||
} // namespace Compile
|
||||
} // namespace Rust
|
||||
|
||||
#endif // RUST_MANGLE_H
|
||||
958
gcc/rust/backend/rust-tree.cc
Normal file
958
gcc/rust/backend/rust-tree.cc
Normal file
@@ -0,0 +1,958 @@
|
||||
// Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
||||
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "rust-tree.h"
|
||||
#include "fold-const.h"
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "escaped_string.h"
|
||||
|
||||
namespace Rust {
|
||||
|
||||
void
|
||||
mark_exp_read (tree exp)
|
||||
{
|
||||
if (exp == NULL)
|
||||
return;
|
||||
|
||||
switch (TREE_CODE (exp))
|
||||
{
|
||||
case VAR_DECL:
|
||||
gcc_fallthrough ();
|
||||
case PARM_DECL:
|
||||
DECL_READ_P (exp) = 1;
|
||||
break;
|
||||
case ARRAY_REF:
|
||||
case COMPONENT_REF:
|
||||
case MODIFY_EXPR:
|
||||
case REALPART_EXPR:
|
||||
case IMAGPART_EXPR:
|
||||
CASE_CONVERT:
|
||||
case ADDR_EXPR:
|
||||
case INDIRECT_REF:
|
||||
case FLOAT_EXPR:
|
||||
case NON_DEPENDENT_EXPR:
|
||||
case VIEW_CONVERT_EXPR:
|
||||
mark_exp_read (TREE_OPERAND (exp, 0));
|
||||
break;
|
||||
case COMPOUND_EXPR:
|
||||
mark_exp_read (TREE_OPERAND (exp, 1));
|
||||
break;
|
||||
case COND_EXPR:
|
||||
if (TREE_OPERAND (exp, 1))
|
||||
mark_exp_read (TREE_OPERAND (exp, 1));
|
||||
if (TREE_OPERAND (exp, 2))
|
||||
mark_exp_read (TREE_OPERAND (exp, 2));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tree
|
||||
convert_from_reference (tree val)
|
||||
{
|
||||
if (TREE_TYPE (val) && TYPE_REF_P (TREE_TYPE (val)))
|
||||
{
|
||||
tree t = TREE_TYPE (TREE_TYPE (val));
|
||||
tree ref = build1 (INDIRECT_REF, t, val);
|
||||
|
||||
mark_exp_read (val);
|
||||
|
||||
TREE_SIDE_EFFECTS (ref)
|
||||
= (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val));
|
||||
val = ref;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
tree
|
||||
mark_use (tree expr, bool rvalue_p, bool read_p,
|
||||
location_t loc /* = UNKNOWN_LOCATION */,
|
||||
bool reject_builtin /* = true */)
|
||||
{
|
||||
#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
|
||||
|
||||
if (expr == NULL_TREE || error_operand_p (expr))
|
||||
return expr;
|
||||
|
||||
if (reject_builtin)
|
||||
return error_mark_node;
|
||||
|
||||
if (read_p)
|
||||
mark_exp_read (expr);
|
||||
|
||||
bool recurse_op[3] = {false, false, false};
|
||||
switch (TREE_CODE (expr))
|
||||
{
|
||||
case COMPONENT_REF:
|
||||
case NON_DEPENDENT_EXPR:
|
||||
recurse_op[0] = true;
|
||||
break;
|
||||
case COMPOUND_EXPR:
|
||||
recurse_op[1] = true;
|
||||
break;
|
||||
case COND_EXPR:
|
||||
recurse_op[2] = true;
|
||||
if (TREE_OPERAND (expr, 1))
|
||||
recurse_op[1] = true;
|
||||
break;
|
||||
case INDIRECT_REF:
|
||||
if (REFERENCE_REF_P (expr))
|
||||
{
|
||||
/* Try to look through the reference. */
|
||||
tree ref = TREE_OPERAND (expr, 0);
|
||||
tree r = mark_rvalue_use (ref, loc, reject_builtin);
|
||||
if (r != ref)
|
||||
expr = convert_from_reference (r);
|
||||
}
|
||||
break;
|
||||
|
||||
case VIEW_CONVERT_EXPR:
|
||||
if (location_wrapper_p (expr))
|
||||
{
|
||||
loc = EXPR_LOCATION (expr);
|
||||
tree op = TREE_OPERAND (expr, 0);
|
||||
tree nop = RECUR (op);
|
||||
if (nop == error_mark_node)
|
||||
return error_mark_node;
|
||||
else if (op == nop)
|
||||
/* No change. */;
|
||||
else if (DECL_P (nop) || CONSTANT_CLASS_P (nop))
|
||||
{
|
||||
/* Reuse the location wrapper. */
|
||||
TREE_OPERAND (expr, 0) = nop;
|
||||
/* If we're replacing a DECL with a constant, we also need to
|
||||
change the TREE_CODE of the location wrapper. */
|
||||
if (rvalue_p)
|
||||
TREE_SET_CODE (expr, NON_LVALUE_EXPR);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Drop the location wrapper. */
|
||||
expr = nop;
|
||||
protected_set_expr_location (expr, loc);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
gcc_fallthrough ();
|
||||
CASE_CONVERT:
|
||||
recurse_op[0] = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
if (recurse_op[i])
|
||||
{
|
||||
tree op = TREE_OPERAND (expr, i);
|
||||
op = RECUR (op);
|
||||
if (op == error_mark_node)
|
||||
return error_mark_node;
|
||||
TREE_OPERAND (expr, i) = op;
|
||||
}
|
||||
|
||||
return expr;
|
||||
#undef RECUR
|
||||
}
|
||||
|
||||
tree
|
||||
mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
|
||||
bool reject_builtin /* = true */)
|
||||
{
|
||||
return mark_use (e, true, true, loc, reject_builtin);
|
||||
}
|
||||
|
||||
tree
|
||||
mark_lvalue_use (tree expr)
|
||||
{
|
||||
return mark_use (expr, false, true, input_location, false);
|
||||
}
|
||||
|
||||
tree
|
||||
mark_lvalue_use_nonread (tree expr)
|
||||
{
|
||||
return mark_use (expr, false, false, input_location, false);
|
||||
}
|
||||
|
||||
tree
|
||||
mark_discarded_use (tree expr)
|
||||
{
|
||||
if (expr == NULL_TREE)
|
||||
return expr;
|
||||
|
||||
STRIP_ANY_LOCATION_WRAPPER (expr);
|
||||
|
||||
switch (TREE_CODE (expr))
|
||||
{
|
||||
case COND_EXPR:
|
||||
TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2));
|
||||
gcc_fallthrough ();
|
||||
case COMPOUND_EXPR:
|
||||
TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1));
|
||||
return expr;
|
||||
|
||||
case COMPONENT_REF:
|
||||
case ARRAY_REF:
|
||||
case INDIRECT_REF:
|
||||
case MEMBER_REF:
|
||||
break;
|
||||
default:
|
||||
if (DECL_P (expr))
|
||||
break;
|
||||
else
|
||||
return expr;
|
||||
}
|
||||
|
||||
return mark_use (expr, true, true, input_location, false);
|
||||
}
|
||||
|
||||
tree
|
||||
convert_to_void (tree expr, impl_conv_void implicit)
|
||||
{
|
||||
location_t loc = expr_loc_or_input_loc (expr);
|
||||
if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
expr = mark_discarded_use (expr);
|
||||
if (implicit == ICV_CAST)
|
||||
/* An explicit cast to void avoids all -Wunused-but-set* warnings. */
|
||||
mark_exp_read (expr);
|
||||
|
||||
if (!TREE_TYPE (expr))
|
||||
return expr;
|
||||
|
||||
if (VOID_TYPE_P (TREE_TYPE (expr)))
|
||||
return expr;
|
||||
switch (TREE_CODE (expr))
|
||||
{
|
||||
case COND_EXPR: {
|
||||
/* The two parts of a cond expr might be separate lvalues. */
|
||||
tree op1 = TREE_OPERAND (expr, 1);
|
||||
tree op2 = TREE_OPERAND (expr, 2);
|
||||
bool side_effects
|
||||
= ((op1 && TREE_SIDE_EFFECTS (op1)) || TREE_SIDE_EFFECTS (op2));
|
||||
tree new_op1, new_op2;
|
||||
new_op1 = NULL_TREE;
|
||||
if (implicit != ICV_CAST && !side_effects)
|
||||
{
|
||||
if (op1)
|
||||
new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND);
|
||||
new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op1)
|
||||
new_op1 = convert_to_void (op1, ICV_CAST);
|
||||
new_op2 = convert_to_void (op2, ICV_CAST);
|
||||
}
|
||||
|
||||
expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2),
|
||||
TREE_OPERAND (expr, 0), new_op1, new_op2);
|
||||
break;
|
||||
}
|
||||
|
||||
case COMPOUND_EXPR: {
|
||||
/* The second part of a compound expr contains the value. */
|
||||
tree op1 = TREE_OPERAND (expr, 1);
|
||||
tree new_op1;
|
||||
if (implicit != ICV_CAST
|
||||
&& !warning_suppressed_p (expr /* What warning? */))
|
||||
new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA);
|
||||
else
|
||||
new_op1 = convert_to_void (op1, ICV_CAST);
|
||||
|
||||
if (new_op1 != op1)
|
||||
{
|
||||
tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1),
|
||||
TREE_OPERAND (expr, 0), new_op1);
|
||||
expr = t;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case NON_LVALUE_EXPR:
|
||||
case NOP_EXPR:
|
||||
/* These have already decayed to rvalue. */
|
||||
break;
|
||||
|
||||
case CALL_EXPR:
|
||||
maybe_warn_nodiscard (expr, implicit);
|
||||
break;
|
||||
|
||||
case INDIRECT_REF: {
|
||||
tree type = TREE_TYPE (expr);
|
||||
int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0)));
|
||||
int is_volatile = TYPE_VOLATILE (type);
|
||||
int is_complete = COMPLETE_TYPE_P (type);
|
||||
|
||||
/* Can't load the value if we don't know the type. */
|
||||
if (is_volatile && !is_complete)
|
||||
{
|
||||
switch (implicit)
|
||||
{
|
||||
case ICV_CAST:
|
||||
warning_at (loc, 0,
|
||||
"conversion to void will not access "
|
||||
"object of incomplete type %qT",
|
||||
type);
|
||||
break;
|
||||
case ICV_SECOND_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"incomplete type %qT in second operand "
|
||||
"of conditional expression",
|
||||
type);
|
||||
break;
|
||||
case ICV_THIRD_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"incomplete type %qT in third operand "
|
||||
"of conditional expression",
|
||||
type);
|
||||
break;
|
||||
case ICV_RIGHT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"incomplete type %qT in right operand of "
|
||||
"comma operator",
|
||||
type);
|
||||
break;
|
||||
case ICV_LEFT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"incomplete type %qT in left operand of "
|
||||
"comma operator",
|
||||
type);
|
||||
break;
|
||||
case ICV_STATEMENT:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"incomplete type %qT in statement",
|
||||
type);
|
||||
break;
|
||||
case ICV_THIRD_IN_FOR:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"incomplete type %qT in for increment "
|
||||
"expression",
|
||||
type);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
/* Don't load the value if this is an implicit dereference, or if
|
||||
the type needs to be handled by ctors/dtors. */
|
||||
else if (is_volatile && is_reference)
|
||||
{
|
||||
switch (implicit)
|
||||
{
|
||||
case ICV_CAST:
|
||||
warning_at (loc, 0,
|
||||
"conversion to void will not access "
|
||||
"object of type %qT",
|
||||
type);
|
||||
break;
|
||||
case ICV_SECOND_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"implicit dereference will not access "
|
||||
"object of type %qT in second operand of "
|
||||
"conditional expression",
|
||||
type);
|
||||
break;
|
||||
case ICV_THIRD_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"implicit dereference will not access "
|
||||
"object of type %qT in third operand of "
|
||||
"conditional expression",
|
||||
type);
|
||||
break;
|
||||
case ICV_RIGHT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"implicit dereference will not access "
|
||||
"object of type %qT in right operand of "
|
||||
"comma operator",
|
||||
type);
|
||||
break;
|
||||
case ICV_LEFT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"implicit dereference will not access "
|
||||
"object of type %qT in left operand of comma "
|
||||
"operator",
|
||||
type);
|
||||
break;
|
||||
case ICV_STATEMENT:
|
||||
warning_at (loc, 0,
|
||||
"implicit dereference will not access "
|
||||
"object of type %qT in statement",
|
||||
type);
|
||||
break;
|
||||
case ICV_THIRD_IN_FOR:
|
||||
warning_at (loc, 0,
|
||||
"implicit dereference will not access "
|
||||
"object of type %qT in for increment expression",
|
||||
type);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
else if (is_volatile && TREE_ADDRESSABLE (type))
|
||||
{
|
||||
switch (implicit)
|
||||
{
|
||||
case ICV_CAST:
|
||||
warning_at (loc, 0,
|
||||
"conversion to void will not access "
|
||||
"object of non-trivially-copyable type %qT",
|
||||
type);
|
||||
break;
|
||||
case ICV_SECOND_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"non-trivially-copyable type %qT in second "
|
||||
"operand of conditional expression",
|
||||
type);
|
||||
break;
|
||||
case ICV_THIRD_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"non-trivially-copyable type %qT in third "
|
||||
"operand of conditional expression",
|
||||
type);
|
||||
break;
|
||||
case ICV_RIGHT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"non-trivially-copyable type %qT in right "
|
||||
"operand of comma operator",
|
||||
type);
|
||||
break;
|
||||
case ICV_LEFT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"non-trivially-copyable type %qT in left "
|
||||
"operand of comma operator",
|
||||
type);
|
||||
break;
|
||||
case ICV_STATEMENT:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"non-trivially-copyable type %qT in statement",
|
||||
type);
|
||||
break;
|
||||
case ICV_THIRD_IN_FOR:
|
||||
warning_at (loc, 0,
|
||||
"indirection will not access object of "
|
||||
"non-trivially-copyable type %qT in for "
|
||||
"increment expression",
|
||||
type);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
if (is_reference || !is_volatile || !is_complete
|
||||
|| TREE_ADDRESSABLE (type))
|
||||
{
|
||||
/* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
|
||||
operation is stripped off. Note that we don't warn about
|
||||
- an expression with TREE_NO_WARNING set. (For an example of
|
||||
such expressions, see build_over_call in call.cc.)
|
||||
- automatic dereferencing of references, since the user cannot
|
||||
control it. (See also warn_if_unused_value() in c-common.cc.)
|
||||
*/
|
||||
if (warn_unused_value && implicit != ICV_CAST
|
||||
&& !warning_suppressed_p (expr, OPT_Wunused_value)
|
||||
&& !is_reference)
|
||||
warning_at (loc, OPT_Wunused_value, "value computed is not used");
|
||||
expr = TREE_OPERAND (expr, 0);
|
||||
if (TREE_CODE (expr) == CALL_EXPR)
|
||||
maybe_warn_nodiscard (expr, implicit);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VAR_DECL: {
|
||||
/* External variables might be incomplete. */
|
||||
tree type = TREE_TYPE (expr);
|
||||
int is_complete = COMPLETE_TYPE_P (type);
|
||||
|
||||
if (TYPE_VOLATILE (type) && !is_complete)
|
||||
switch (implicit)
|
||||
{
|
||||
case ICV_CAST:
|
||||
warning_at (loc, 0,
|
||||
"conversion to void will not access "
|
||||
"object %qE of incomplete type %qT",
|
||||
expr, type);
|
||||
break;
|
||||
case ICV_SECOND_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"variable %qE of incomplete type %qT will "
|
||||
"not be accessed in second operand of "
|
||||
"conditional expression",
|
||||
expr, type);
|
||||
break;
|
||||
case ICV_THIRD_OF_COND:
|
||||
warning_at (loc, 0,
|
||||
"variable %qE of incomplete type %qT will "
|
||||
"not be accessed in third operand of "
|
||||
"conditional expression",
|
||||
expr, type);
|
||||
break;
|
||||
case ICV_RIGHT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"variable %qE of incomplete type %qT will "
|
||||
"not be accessed in right operand of comma operator",
|
||||
expr, type);
|
||||
break;
|
||||
case ICV_LEFT_OF_COMMA:
|
||||
warning_at (loc, 0,
|
||||
"variable %qE of incomplete type %qT will "
|
||||
"not be accessed in left operand of comma operator",
|
||||
expr, type);
|
||||
break;
|
||||
case ICV_STATEMENT:
|
||||
warning_at (loc, 0,
|
||||
"variable %qE of incomplete type %qT will "
|
||||
"not be accessed in statement",
|
||||
expr, type);
|
||||
break;
|
||||
case ICV_THIRD_IN_FOR:
|
||||
warning_at (loc, 0,
|
||||
"variable %qE of incomplete type %qT will "
|
||||
"not be accessed in for increment expression",
|
||||
expr, type);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
if (!TREE_SIDE_EFFECTS (expr))
|
||||
expr = void_node;
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
void
|
||||
maybe_warn_nodiscard (tree expr, impl_conv_void implicit)
|
||||
{
|
||||
tree call = expr;
|
||||
if (TREE_CODE (expr) == TARGET_EXPR)
|
||||
call = TARGET_EXPR_INITIAL (expr);
|
||||
|
||||
location_t loc = expr_loc_or_input_loc (call);
|
||||
tree callee = CALL_EXPR_FN (call);
|
||||
if (!callee)
|
||||
return;
|
||||
|
||||
tree type = TREE_TYPE (callee);
|
||||
if (INDIRECT_TYPE_P (type))
|
||||
type = TREE_TYPE (type);
|
||||
|
||||
tree rettype = TREE_TYPE (type);
|
||||
tree fn = get_fndecl_from_callee (callee);
|
||||
tree attr;
|
||||
if (implicit != ICV_CAST && fn
|
||||
&& (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn))))
|
||||
{
|
||||
escaped_string msg;
|
||||
tree args = TREE_VALUE (attr);
|
||||
if (args)
|
||||
msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
|
||||
const char *format
|
||||
= (msg ? G_ ("ignoring return value of %qD, that must be used: %<%s%>")
|
||||
: G_ ("ignoring return value of %qD, that must be used"));
|
||||
const char *raw_msg = msg ? (const char *) msg : "";
|
||||
auto_diagnostic_group d;
|
||||
if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg))
|
||||
inform (DECL_SOURCE_LOCATION (fn), "declared here");
|
||||
}
|
||||
else if (implicit != ICV_CAST
|
||||
&& (attr
|
||||
= lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype))))
|
||||
{
|
||||
escaped_string msg;
|
||||
tree args = TREE_VALUE (attr);
|
||||
if (args)
|
||||
msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
|
||||
const char *format
|
||||
= (msg ? G_ (
|
||||
"ignoring returned value of type %qT, that must be used: %<%s%>")
|
||||
: G_ ("ignoring returned value of type %qT, that must be used"));
|
||||
const char *raw_msg = msg ? (const char *) msg : "";
|
||||
auto_diagnostic_group d;
|
||||
if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg))
|
||||
{
|
||||
if (fn)
|
||||
inform (DECL_SOURCE_LOCATION (fn), "in call to %qD, declared here",
|
||||
fn);
|
||||
inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
|
||||
"%qT declared here", rettype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
location_t
|
||||
expr_loc_or_loc (const_tree t, location_t or_loc)
|
||||
{
|
||||
location_t loc = EXPR_LOCATION (t);
|
||||
if (loc == UNKNOWN_LOCATION)
|
||||
loc = or_loc;
|
||||
return loc;
|
||||
}
|
||||
|
||||
location_t
|
||||
expr_loc_or_input_loc (const_tree t)
|
||||
{
|
||||
return expr_loc_or_loc (t, input_location);
|
||||
}
|
||||
|
||||
// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
|
||||
// if we can.
|
||||
tree
|
||||
get_fndecl_from_callee (tree fn)
|
||||
{
|
||||
if (fn == NULL_TREE)
|
||||
return fn;
|
||||
if (TREE_CODE (fn) == FUNCTION_DECL)
|
||||
return fn;
|
||||
tree type = TREE_TYPE (fn);
|
||||
if (type == NULL_TREE || !INDIRECT_TYPE_P (type))
|
||||
return NULL_TREE;
|
||||
|
||||
STRIP_NOPS (fn);
|
||||
if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR)
|
||||
fn = TREE_OPERAND (fn, 0);
|
||||
if (TREE_CODE (fn) == FUNCTION_DECL)
|
||||
return fn;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree
|
||||
pointer_offset_expression (tree base_tree, tree index_tree, location_t location)
|
||||
{
|
||||
tree element_type_tree = TREE_TYPE (TREE_TYPE (base_tree));
|
||||
if (base_tree == error_mark_node || TREE_TYPE (base_tree) == error_mark_node
|
||||
|| index_tree == error_mark_node || element_type_tree == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
tree element_size = TYPE_SIZE_UNIT (element_type_tree);
|
||||
index_tree = fold_convert_loc (location, sizetype, index_tree);
|
||||
tree offset
|
||||
= fold_build2_loc (location, MULT_EXPR, sizetype, index_tree, element_size);
|
||||
|
||||
return fold_build2_loc (location, POINTER_PLUS_EXPR, TREE_TYPE (base_tree),
|
||||
base_tree, offset);
|
||||
}
|
||||
|
||||
// forked from gcc/cp/tree.cc cp_walk_subtrees
|
||||
/* Apply FUNC to all language-specific sub-trees of TP in a pre-order
|
||||
traversal. Called from walk_tree. */
|
||||
|
||||
tree
|
||||
rs_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, void *data,
|
||||
hash_set<tree> *pset)
|
||||
{
|
||||
enum tree_code code = TREE_CODE (*tp);
|
||||
tree result;
|
||||
|
||||
#define WALK_SUBTREE(NODE) \
|
||||
do \
|
||||
{ \
|
||||
result = rs_walk_tree (&(NODE), func, data, pset); \
|
||||
if (result) \
|
||||
goto out; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
if (TYPE_P (*tp))
|
||||
{
|
||||
/* If *WALK_SUBTREES_P is 1, we're interested in the syntactic form of
|
||||
the argument, so don't look through typedefs, but do walk into
|
||||
template arguments for alias templates (and non-typedefed classes).
|
||||
|
||||
If *WALK_SUBTREES_P > 1, we're interested in type identity or
|
||||
equivalence, so look through typedefs, ignoring template arguments for
|
||||
alias templates, and walk into template args of classes.
|
||||
|
||||
See find_abi_tags_r for an example of setting *WALK_SUBTREES_P to 2
|
||||
when that's the behavior the walk_tree_fn wants. */
|
||||
if (*walk_subtrees_p == 1 && typedef_variant_p (*tp))
|
||||
{
|
||||
*walk_subtrees_p = 0;
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not one of the easy cases. We must explicitly go through the
|
||||
children. */
|
||||
result = NULL_TREE;
|
||||
switch (code)
|
||||
{
|
||||
case TREE_LIST:
|
||||
WALK_SUBTREE (TREE_PURPOSE (*tp));
|
||||
break;
|
||||
|
||||
case RECORD_TYPE:
|
||||
if (TYPE_PTRMEMFUNC_P (*tp))
|
||||
WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp));
|
||||
break;
|
||||
|
||||
case CONSTRUCTOR:
|
||||
if (COMPOUND_LITERAL_P (*tp))
|
||||
WALK_SUBTREE (TREE_TYPE (*tp));
|
||||
break;
|
||||
|
||||
case DECL_EXPR:
|
||||
/* User variables should be mentioned in BIND_EXPR_VARS
|
||||
and their initializers and sizes walked when walking
|
||||
the containing BIND_EXPR. Compiler temporaries are
|
||||
handled here. And also normal variables in templates,
|
||||
since do_poplevel doesn't build a BIND_EXPR then. */
|
||||
if (VAR_P (TREE_OPERAND (*tp, 0))
|
||||
&& (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
|
||||
&& !TREE_STATIC (TREE_OPERAND (*tp, 0))))
|
||||
{
|
||||
tree decl = TREE_OPERAND (*tp, 0);
|
||||
WALK_SUBTREE (DECL_INITIAL (decl));
|
||||
WALK_SUBTREE (DECL_SIZE (decl));
|
||||
WALK_SUBTREE (DECL_SIZE_UNIT (decl));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* We didn't find what we were looking for. */
|
||||
out:
|
||||
return result;
|
||||
|
||||
#undef WALK_SUBTREE
|
||||
}
|
||||
|
||||
// forked from gcc/cp/tree.cc cp_expr_location
|
||||
|
||||
/* Like EXPR_LOCATION, but also handle some tcc_exceptional that have
|
||||
locations. */
|
||||
|
||||
location_t
|
||||
rs_expr_location (const_tree t_)
|
||||
{
|
||||
tree t = CONST_CAST_TREE (t_);
|
||||
if (t == NULL_TREE)
|
||||
return UNKNOWN_LOCATION;
|
||||
|
||||
return EXPR_LOCATION (t);
|
||||
}
|
||||
|
||||
// forked from gcc/cp/class.cc is_really_empty_class
|
||||
|
||||
/* Returns true if TYPE contains no actual data, just various
|
||||
possible combinations of empty classes. If IGNORE_VPTR is true,
|
||||
a vptr doesn't prevent the class from being considered empty. Typically
|
||||
we want to ignore the vptr on assignment, and not on initialization. */
|
||||
|
||||
bool
|
||||
is_really_empty_class (tree type, bool ignore_vptr)
|
||||
{
|
||||
if (CLASS_TYPE_P (type))
|
||||
{
|
||||
tree field;
|
||||
tree binfo;
|
||||
tree base_binfo;
|
||||
int i;
|
||||
|
||||
/* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid
|
||||
out, but we'd like to be able to check this before then. */
|
||||
if (COMPLETE_TYPE_P (type) && is_empty_class (type))
|
||||
return true;
|
||||
|
||||
if (!ignore_vptr && TYPE_CONTAINS_VPTR_P (type))
|
||||
return false;
|
||||
|
||||
for (binfo = TYPE_BINFO (type), i = 0;
|
||||
BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
|
||||
if (!is_really_empty_class (BINFO_TYPE (base_binfo), ignore_vptr))
|
||||
return false;
|
||||
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
|
||||
if (TREE_CODE (field) == FIELD_DECL
|
||||
&& !DECL_ARTIFICIAL (field)
|
||||
/* An unnamed bit-field is not a data member. */
|
||||
&& !DECL_UNNAMED_BIT_FIELD (field)
|
||||
&& !is_really_empty_class (TREE_TYPE (field), ignore_vptr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
return (integer_zerop (array_type_nelts_top (type))
|
||||
|| is_really_empty_class (TREE_TYPE (type), ignore_vptr));
|
||||
return false;
|
||||
}
|
||||
|
||||
// forked from gcc/cp/class.cc is_empty_class
|
||||
|
||||
/* Returns 1 if TYPE contains only padding bytes. */
|
||||
|
||||
int
|
||||
is_empty_class (tree type)
|
||||
{
|
||||
if (type == error_mark_node)
|
||||
return 0;
|
||||
|
||||
if (!CLASS_TYPE_P (type))
|
||||
return 0;
|
||||
|
||||
return CLASSTYPE_EMPTY_P (type);
|
||||
}
|
||||
|
||||
// forked from gcc/cp/tree.cc array_type_nelts_top
|
||||
|
||||
/* Return, as an INTEGER_CST node, the number of elements for TYPE
|
||||
(which is an ARRAY_TYPE). This counts only elements of the top
|
||||
array. */
|
||||
|
||||
tree
|
||||
array_type_nelts_top (tree type)
|
||||
{
|
||||
return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
|
||||
array_type_nelts (type), size_one_node);
|
||||
}
|
||||
|
||||
// forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p
|
||||
|
||||
/* Test whether DECL is a builtin that may appear in a
|
||||
constant-expression. */
|
||||
|
||||
bool
|
||||
builtin_valid_in_constant_expr_p (const_tree decl)
|
||||
{
|
||||
STRIP_ANY_LOCATION_WRAPPER (decl);
|
||||
if (TREE_CODE (decl) != FUNCTION_DECL)
|
||||
/* Not a function. */
|
||||
return false;
|
||||
if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
|
||||
{
|
||||
if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
|
||||
switch (DECL_FE_FUNCTION_CODE (decl))
|
||||
{
|
||||
case RS_BUILT_IN_IS_CONSTANT_EVALUATED:
|
||||
case RS_BUILT_IN_SOURCE_LOCATION:
|
||||
case RS_BUILT_IN_IS_CORRESPONDING_MEMBER:
|
||||
case RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Not a built-in. */
|
||||
return false;
|
||||
}
|
||||
switch (DECL_FUNCTION_CODE (decl))
|
||||
{
|
||||
/* These always have constant results like the corresponding
|
||||
macros/symbol. */
|
||||
case BUILT_IN_FILE:
|
||||
case BUILT_IN_FUNCTION:
|
||||
case BUILT_IN_LINE:
|
||||
|
||||
/* The following built-ins are valid in constant expressions
|
||||
when their arguments are. */
|
||||
case BUILT_IN_ADD_OVERFLOW_P:
|
||||
case BUILT_IN_SUB_OVERFLOW_P:
|
||||
case BUILT_IN_MUL_OVERFLOW_P:
|
||||
|
||||
/* These have constant results even if their operands are
|
||||
non-constant. */
|
||||
case BUILT_IN_CONSTANT_P:
|
||||
case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// forked from gcc/cp/decl2.cc decl_maybe_constant_var_p
|
||||
|
||||
/* Returns true if DECL could be a symbolic constant variable, depending on
|
||||
its initializer. */
|
||||
|
||||
bool
|
||||
decl_maybe_constant_var_p (tree decl)
|
||||
{
|
||||
tree type = TREE_TYPE (decl);
|
||||
if (!VAR_P (decl))
|
||||
return false;
|
||||
if (DECL_DECLARED_CONSTEXPR_P (decl))
|
||||
return true;
|
||||
if (DECL_HAS_VALUE_EXPR_P (decl))
|
||||
/* A proxy isn't constant. */
|
||||
return false;
|
||||
if (TYPE_REF_P (type))
|
||||
/* References can be constant. */;
|
||||
else if (RS_TYPE_CONST_NON_VOLATILE_P (type)
|
||||
&& INTEGRAL_OR_ENUMERATION_TYPE_P (type))
|
||||
/* And const integers. */;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (DECL_INITIAL (decl) && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
|
||||
/* We know the initializer, and it isn't constant. */
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// forked from gcc/cp/typeck.cc cp_type_quals
|
||||
|
||||
/* Returns the type qualifiers for this type, including the qualifiers on the
|
||||
elements for an array type. */
|
||||
|
||||
int
|
||||
rs_type_quals (const_tree type)
|
||||
{
|
||||
int quals;
|
||||
/* This CONST_CAST is okay because strip_array_types returns its
|
||||
argument unmodified and we assign it to a const_tree. */
|
||||
type = strip_array_types (CONST_CAST_TREE (type));
|
||||
if (type == error_mark_node
|
||||
/* Quals on a FUNCTION_TYPE are memfn quals. */
|
||||
|| TREE_CODE (type) == FUNCTION_TYPE)
|
||||
return TYPE_UNQUALIFIED;
|
||||
quals = TYPE_QUALS (type);
|
||||
/* METHOD and REFERENCE_TYPEs should never have quals. */
|
||||
gcc_assert (
|
||||
(TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type))
|
||||
|| ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) == TYPE_UNQUALIFIED));
|
||||
return quals;
|
||||
}
|
||||
|
||||
} // namespace Rust
|
||||
508
gcc/rust/backend/rust-tree.h
Normal file
508
gcc/rust/backend/rust-tree.h
Normal file
@@ -0,0 +1,508 @@
|
||||
// Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
||||
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef RUST_TREE
|
||||
#define RUST_TREE
|
||||
|
||||
#include "rust-system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
|
||||
/* Returns true if NODE is a pointer. */
|
||||
#define TYPE_PTR_P(NODE) (TREE_CODE (NODE) == POINTER_TYPE)
|
||||
|
||||
/* Returns true if NODE is a reference. */
|
||||
#define TYPE_REF_P(NODE) (TREE_CODE (NODE) == REFERENCE_TYPE)
|
||||
|
||||
/* Returns true if NODE is a pointer or a reference. */
|
||||
#define INDIRECT_TYPE_P(NODE) (TYPE_PTR_P (NODE) || TYPE_REF_P (NODE))
|
||||
|
||||
/* [basic.fundamental]
|
||||
|
||||
Types bool, char, wchar_t, and the signed and unsigned integer types
|
||||
are collectively called integral types.
|
||||
|
||||
Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
|
||||
types as well, which is incorrect in C++. Keep these checks in
|
||||
ascending code order. */
|
||||
#define RS_INTEGRAL_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
|
||||
|
||||
/* [basic.fundamental]
|
||||
|
||||
Integral and floating types are collectively called arithmetic
|
||||
types.
|
||||
|
||||
As a GNU extension, we also accept complex types.
|
||||
|
||||
Keep these checks in ascending code order. */
|
||||
#define ARITHMETIC_TYPE_P(TYPE) \
|
||||
(RS_INTEGRAL_TYPE_P (TYPE) || TREE_CODE (TYPE) == REAL_TYPE \
|
||||
|| TREE_CODE (TYPE) == COMPLEX_TYPE)
|
||||
|
||||
/* True iff TYPE is cv decltype(nullptr). */
|
||||
#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE)
|
||||
|
||||
/* [basic.types]
|
||||
|
||||
Arithmetic types, enumeration types, pointer types,
|
||||
pointer-to-member types, and std::nullptr_t are collectively called
|
||||
scalar types.
|
||||
|
||||
Keep these checks in ascending code order. */
|
||||
#define SCALAR_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == ENUMERAL_TYPE || ARITHMETIC_TYPE_P (TYPE) \
|
||||
|| TYPE_PTR_P (TYPE) || NULLPTR_TYPE_P (TYPE))
|
||||
|
||||
/* True if NODE is an implicit INDIRECT_REF from convert_from_reference. */
|
||||
#define REFERENCE_REF_P(NODE) \
|
||||
(INDIRECT_REF_P (NODE) && TREE_TYPE (TREE_OPERAND (NODE, 0)) \
|
||||
&& TYPE_REF_P (TREE_TYPE (TREE_OPERAND ((NODE), 0))))
|
||||
|
||||
// this is a helper to differentiate RECORD types between actual records and
|
||||
// slices
|
||||
#define SLICE_FLAG TREE_LANG_FLAG_0
|
||||
#define SLICE_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == RECORD_TYPE && TREE_LANG_FLAG_0 (TYPE))
|
||||
|
||||
/* Returns true if NODE is a pointer to member function type. */
|
||||
#define TYPE_PTRMEMFUNC_P(NODE) \
|
||||
(TREE_CODE (NODE) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (NODE))
|
||||
|
||||
#define TYPE_PTRMEMFUNC_FLAG(NODE) (TYPE_LANG_FLAG_2 (RECORD_TYPE_CHECK (NODE)))
|
||||
|
||||
#define TYPE_PTRMEMFUNC_FN_TYPE_RAW(NODE) (TREE_TYPE (TYPE_FIELDS (NODE)))
|
||||
|
||||
/* True if NODE is a compound-literal, i.e., a brace-enclosed
|
||||
initializer cast to a particular type. This is mostly only set during
|
||||
template parsing; once the initializer has been digested into an actual
|
||||
value of the type, the expression is represented by a TARGET_EXPR. */
|
||||
#define COMPOUND_LITERAL_P(NODE) \
|
||||
(TREE_CODE (NODE) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (NODE))
|
||||
|
||||
/* When appearing in an INDIRECT_REF, it means that the tree structure
|
||||
underneath is actually a call to a constructor. This is needed
|
||||
when the constructor must initialize local storage (which can
|
||||
be automatically destroyed), rather than allowing it to allocate
|
||||
space from the heap.
|
||||
|
||||
When appearing in a SAVE_EXPR, it means that underneath
|
||||
is a call to a constructor.
|
||||
|
||||
When appearing in a CONSTRUCTOR, the expression is an unconverted
|
||||
compound literal.
|
||||
|
||||
When appearing in a FIELD_DECL, it means that this field
|
||||
has been duly initialized in its constructor. */
|
||||
#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4 (NODE))
|
||||
|
||||
/* Nonzero if T is a class type. Zero for template type parameters,
|
||||
typename types, and so forth. */
|
||||
#define CLASS_TYPE_P(T) \
|
||||
(RECORD_OR_UNION_CODE_P (TREE_CODE (T)) && TYPE_LANG_FLAG_5 (T))
|
||||
|
||||
/* [class.virtual]
|
||||
|
||||
A class that declares or inherits a virtual function is called a
|
||||
polymorphic class. */
|
||||
#define TYPE_POLYMORPHIC_P(NODE) (TREE_LANG_FLAG_2 (NODE))
|
||||
|
||||
/* Nonzero if this class has a virtual function table pointer. */
|
||||
#define TYPE_CONTAINS_VPTR_P(NODE) \
|
||||
(TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE))
|
||||
|
||||
/* A vector of BINFOs for the direct and indirect virtual base classes
|
||||
that this type uses in a post-order depth-first left-to-right
|
||||
order. (In other words, these bases appear in the order that they
|
||||
should be initialized.) */
|
||||
#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
|
||||
|
||||
/* A vector of BINFOs for the direct and indirect virtual base classes
|
||||
that this type uses in a post-order depth-first left-to-right
|
||||
order. (In other words, these bases appear in the order that they
|
||||
should be initialized.) */
|
||||
#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
|
||||
|
||||
/* We used to have a variant type for lang_type. Keep the name of the
|
||||
checking accessor for the sole survivor. */
|
||||
#define LANG_TYPE_CLASS_CHECK(NODE) (TYPE_LANG_SPECIFIC (NODE))
|
||||
|
||||
/* Keep these checks in ascending code order. */
|
||||
#define RECORD_OR_UNION_CODE_P(T) ((T) == RECORD_TYPE || (T) == UNION_TYPE)
|
||||
#define OVERLOAD_TYPE_P(T) (CLASS_TYPE_P (T) || TREE_CODE (T) == ENUMERAL_TYPE)
|
||||
|
||||
/* Nonzero if this class is "empty" in the sense of the C++ ABI. */
|
||||
#define CLASSTYPE_EMPTY_P(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->empty_p)
|
||||
|
||||
/* True if DECL is declared 'constexpr'. */
|
||||
#define DECL_DECLARED_CONSTEXPR_P(DECL) \
|
||||
DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (DECL))
|
||||
|
||||
#define VAR_OR_FUNCTION_DECL_CHECK(NODE) \
|
||||
TREE_CHECK2 (NODE, VAR_DECL, FUNCTION_DECL)
|
||||
|
||||
// Below macros are copied from gcc/c-family/c-common.h
|
||||
|
||||
/* In a FIELD_DECL, nonzero if the decl was originally a bitfield. */
|
||||
#define DECL_C_BIT_FIELD(NODE) (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1)
|
||||
#define SET_DECL_C_BIT_FIELD(NODE) \
|
||||
(DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 1)
|
||||
#define CLEAR_DECL_C_BIT_FIELD(NODE) \
|
||||
(DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 0)
|
||||
|
||||
/* True if the decl was an unnamed bitfield. */
|
||||
#define DECL_UNNAMED_BIT_FIELD(NODE) \
|
||||
(DECL_C_BIT_FIELD (NODE) && !DECL_NAME (NODE))
|
||||
|
||||
/* 1 iff NODE is function-local. */
|
||||
#define DECL_FUNCTION_SCOPE_P(NODE) \
|
||||
(DECL_CONTEXT (NODE) && TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL)
|
||||
|
||||
/* Nonzero if this type is const-qualified, but not
|
||||
volatile-qualified. Other qualifiers are ignored. This macro is
|
||||
used to test whether or not it is OK to bind an rvalue to a
|
||||
reference. */
|
||||
#define RS_TYPE_CONST_NON_VOLATILE_P(NODE) \
|
||||
((rs_type_quals (NODE) & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) \
|
||||
== TYPE_QUAL_CONST)
|
||||
|
||||
/* [basic.fundamental]
|
||||
|
||||
Types bool, char, wchar_t, and the signed and unsigned integer types
|
||||
are collectively called integral types.
|
||||
|
||||
Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
|
||||
types as well, which is incorrect in C++. Keep these checks in
|
||||
ascending code order. */
|
||||
#define RS_INTEGRAL_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
|
||||
|
||||
/* Returns true if TYPE is an integral or enumeration name. Keep
|
||||
these checks in ascending code order. */
|
||||
#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == ENUMERAL_TYPE || RS_INTEGRAL_TYPE_P (TYPE))
|
||||
|
||||
/* Nonzero for a VAR_DECL that was initialized with a
|
||||
constant-expression. */
|
||||
#define DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P(NODE) \
|
||||
(TREE_LANG_FLAG_2 (VAR_DECL_CHECK (NODE)))
|
||||
|
||||
// Above macros are copied from gcc/c-family/c-common.h
|
||||
|
||||
// forked from gcc/cp/cp-tree.h treee_pair_s
|
||||
|
||||
struct GTY (()) tree_pair_s
|
||||
{
|
||||
tree purpose;
|
||||
tree value;
|
||||
};
|
||||
|
||||
// forked from gcc/cp/cp-tree.h tree_pair_p
|
||||
|
||||
typedef tree_pair_s *tree_pair_p;
|
||||
|
||||
// forked from gcc/cp/cp-tree.h lang_type
|
||||
|
||||
/* This structure provides additional information above and beyond
|
||||
what is provide in the ordinary tree_type. In the past, we used it
|
||||
for the types of class types, template parameters types, typename
|
||||
types, and so forth. However, there can be many (tens to hundreds
|
||||
of thousands) of template parameter types in a compilation, and
|
||||
there's no need for this additional information in that case.
|
||||
Therefore, we now use this data structure only for class types.
|
||||
|
||||
In the past, it was thought that there would be relatively few
|
||||
class types. However, in the presence of heavy use of templates,
|
||||
many (i.e., thousands) of classes can easily be generated.
|
||||
Therefore, we should endeavor to keep the size of this structure to
|
||||
a minimum. */
|
||||
struct GTY (()) lang_type
|
||||
{
|
||||
unsigned char align;
|
||||
|
||||
unsigned has_type_conversion : 1;
|
||||
unsigned has_copy_ctor : 1;
|
||||
unsigned has_default_ctor : 1;
|
||||
unsigned const_needs_init : 1;
|
||||
unsigned ref_needs_init : 1;
|
||||
unsigned has_const_copy_assign : 1;
|
||||
unsigned use_template : 2;
|
||||
|
||||
unsigned has_mutable : 1;
|
||||
unsigned com_interface : 1;
|
||||
unsigned non_pod_class : 1;
|
||||
unsigned nearly_empty_p : 1;
|
||||
unsigned user_align : 1;
|
||||
unsigned has_copy_assign : 1;
|
||||
unsigned has_new : 1;
|
||||
unsigned has_array_new : 1;
|
||||
|
||||
unsigned gets_delete : 2;
|
||||
unsigned interface_only : 1;
|
||||
unsigned interface_unknown : 1;
|
||||
unsigned contains_empty_class_p : 1;
|
||||
unsigned anon_aggr : 1;
|
||||
unsigned non_zero_init : 1;
|
||||
unsigned empty_p : 1;
|
||||
/* 32 bits allocated. */
|
||||
|
||||
unsigned vec_new_uses_cookie : 1;
|
||||
unsigned declared_class : 1;
|
||||
unsigned diamond_shaped : 1;
|
||||
unsigned repeated_base : 1;
|
||||
unsigned being_defined : 1;
|
||||
unsigned debug_requested : 1;
|
||||
unsigned fields_readonly : 1;
|
||||
unsigned ptrmemfunc_flag : 1;
|
||||
|
||||
unsigned lazy_default_ctor : 1;
|
||||
unsigned lazy_copy_ctor : 1;
|
||||
unsigned lazy_copy_assign : 1;
|
||||
unsigned lazy_destructor : 1;
|
||||
unsigned has_const_copy_ctor : 1;
|
||||
unsigned has_complex_copy_ctor : 1;
|
||||
unsigned has_complex_copy_assign : 1;
|
||||
unsigned non_aggregate : 1;
|
||||
|
||||
unsigned has_complex_dflt : 1;
|
||||
unsigned has_list_ctor : 1;
|
||||
unsigned non_std_layout : 1;
|
||||
unsigned is_literal : 1;
|
||||
unsigned lazy_move_ctor : 1;
|
||||
unsigned lazy_move_assign : 1;
|
||||
unsigned has_complex_move_ctor : 1;
|
||||
unsigned has_complex_move_assign : 1;
|
||||
|
||||
unsigned has_constexpr_ctor : 1;
|
||||
unsigned unique_obj_representations : 1;
|
||||
unsigned unique_obj_representations_set : 1;
|
||||
bool erroneous : 1;
|
||||
bool non_pod_aggregate : 1;
|
||||
|
||||
/* When adding a flag here, consider whether or not it ought to
|
||||
apply to a template instance if it applies to the template. If
|
||||
so, make sure to copy it in instantiate_class_template! */
|
||||
|
||||
/* There are some bits left to fill out a 32-bit word. Keep track
|
||||
of this by updating the size of this bitfield whenever you add or
|
||||
remove a flag. */
|
||||
unsigned dummy : 3;
|
||||
|
||||
tree primary_base;
|
||||
vec<tree_pair_s, va_gc> *vcall_indices;
|
||||
tree vtables;
|
||||
tree typeinfo_var;
|
||||
vec<tree, va_gc> *vbases;
|
||||
tree as_base;
|
||||
vec<tree, va_gc> *pure_virtuals;
|
||||
tree friend_classes;
|
||||
vec<tree, va_gc> *GTY ((reorder ("resort_type_member_vec"))) members;
|
||||
tree key_method;
|
||||
tree decl_list;
|
||||
tree befriending_classes;
|
||||
/* In a RECORD_TYPE, information specific to Objective-C++, such
|
||||
as a list of adopted protocols or a pointer to a corresponding
|
||||
@interface. See objc/objc-act.h for details. */
|
||||
tree objc_info;
|
||||
/* FIXME reuse another field? */
|
||||
tree lambda_expr;
|
||||
};
|
||||
|
||||
namespace Rust {
|
||||
|
||||
// forked from gcc/cp/cp-tree.h tsubst_flags_t
|
||||
|
||||
/* This type is used for parameters and variables which hold
|
||||
combinations of the flags in enum tsubst_flags. */
|
||||
typedef int tsubst_flags_t;
|
||||
|
||||
// forked from gcc/cp/cvt.cc convert_to_void
|
||||
//
|
||||
// When an expression is used in a void context, its value is discarded and
|
||||
// no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
|
||||
// stmt.expr/1, expr.comma/1]. This permits dereferencing an incomplete type
|
||||
// in a void context. The C++ standard does not define what an `access' to an
|
||||
// object is, but there is reason to believe that it is the lvalue to rvalue
|
||||
// conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
|
||||
// accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
|
||||
// indicates that volatile semantics should be the same between C and C++
|
||||
// where ever possible. C leaves it implementation defined as to what
|
||||
// constitutes an access to a volatile. So, we interpret `*vp' as a read of
|
||||
// the volatile object `vp' points to, unless that is an incomplete type. For
|
||||
// volatile references we do not do this interpretation, because that would
|
||||
// make it impossible to ignore the reference return value from functions. We
|
||||
// issue warnings in the confusing cases.
|
||||
//
|
||||
// The IMPLICIT is ICV_CAST when the user is explicitly converting an
|
||||
// expression to void via a cast. If an expression is being implicitly
|
||||
// converted, IMPLICIT indicates the context of the implicit conversion.
|
||||
|
||||
/* Possible cases of implicit or explicit bad conversions to void. */
|
||||
enum impl_conv_void
|
||||
{
|
||||
ICV_CAST, /* (explicit) conversion to void */
|
||||
ICV_SECOND_OF_COND, /* second operand of conditional expression */
|
||||
ICV_THIRD_OF_COND, /* third operand of conditional expression */
|
||||
ICV_RIGHT_OF_COMMA, /* right operand of comma operator */
|
||||
ICV_LEFT_OF_COMMA, /* left operand of comma operator */
|
||||
ICV_STATEMENT, /* statement */
|
||||
ICV_THIRD_IN_FOR /* for increment expression */
|
||||
};
|
||||
|
||||
/* BUILT_IN_FRONTEND function codes. */
|
||||
enum rs_built_in_function
|
||||
{
|
||||
RS_BUILT_IN_IS_CONSTANT_EVALUATED,
|
||||
RS_BUILT_IN_INTEGER_PACK,
|
||||
RS_BUILT_IN_IS_CORRESPONDING_MEMBER,
|
||||
RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
|
||||
RS_BUILT_IN_SOURCE_LOCATION,
|
||||
RS_BUILT_IN_LAST
|
||||
};
|
||||
|
||||
extern tree
|
||||
convert_to_void (tree expr, impl_conv_void implicit);
|
||||
|
||||
// The lvalue-to-rvalue conversion (7.1) is applied if and only if the
|
||||
// expression is a glvalue of volatile-qualified type and it is one of the
|
||||
// following:
|
||||
// * ( expression ), where expression is one of these expressions,
|
||||
// * id-expression (8.1.4),
|
||||
// * subscripting (8.2.1),
|
||||
// * class member access (8.2.5),
|
||||
// * indirection (8.3.1),
|
||||
// * pointer-to-member operation (8.5),
|
||||
// * conditional expression (8.16) where both the second and the third
|
||||
// operands are one of these expressions, or
|
||||
// * comma expression (8.19) where the right operand is one of these
|
||||
// expressions.
|
||||
extern tree
|
||||
mark_discarded_use (tree expr);
|
||||
|
||||
// Mark EXP as read, not just set, for set but not used -Wunused warning
|
||||
// purposes.
|
||||
extern void
|
||||
mark_exp_read (tree exp);
|
||||
|
||||
// We've seen an actual use of EXPR. Possibly replace an outer variable
|
||||
// reference inside with its constant value or a lambda capture.
|
||||
extern tree
|
||||
mark_use (tree expr, bool rvalue_p, bool read_p, location_t loc,
|
||||
bool reject_builtin);
|
||||
|
||||
// Called whenever the expression EXPR is used in an rvalue context.
|
||||
// When REJECT_BUILTIN is true the expression is checked to make sure
|
||||
// it doesn't make it possible to obtain the address of a GCC built-in
|
||||
// function with no library fallback (or any of its bits, such as in
|
||||
// a conversion to bool).
|
||||
extern tree
|
||||
mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
|
||||
bool reject_builtin /* = true */);
|
||||
|
||||
// Called whenever an expression is used in an lvalue context.
|
||||
extern tree
|
||||
mark_lvalue_use (tree expr);
|
||||
|
||||
// As above, but don't consider this use a read.
|
||||
extern tree
|
||||
mark_lvalue_use_nonread (tree expr);
|
||||
|
||||
// We are using a reference VAL for its value. Bash that reference all the way
|
||||
// down to its lowest form.
|
||||
extern tree
|
||||
convert_from_reference (tree val);
|
||||
|
||||
// Subroutine of convert_to_void. Warn if we're discarding something with
|
||||
// attribute [[nodiscard]].
|
||||
extern void
|
||||
maybe_warn_nodiscard (tree expr, impl_conv_void implicit);
|
||||
|
||||
extern location_t
|
||||
expr_loc_or_loc (const_tree t, location_t or_loc);
|
||||
|
||||
extern location_t
|
||||
expr_loc_or_input_loc (const_tree t);
|
||||
|
||||
// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
|
||||
// if we can.
|
||||
extern tree
|
||||
get_fndecl_from_callee (tree fn);
|
||||
|
||||
// FIXME some helpers from HIRCompileBase could probably be moved here over time
|
||||
|
||||
// Return an expression for the address of BASE[INDEX], used in offset intrinsic
|
||||
extern tree
|
||||
pointer_offset_expression (tree base_tree, tree index_tree, location_t locus);
|
||||
|
||||
/* A tree node, together with a location, so that we can track locations
|
||||
(and ranges) during parsing.
|
||||
|
||||
The location is redundant for node kinds that have locations,
|
||||
but not all node kinds do (e.g. constants, and references to
|
||||
params, locals, etc), so we stash a copy here. */
|
||||
|
||||
extern location_t rs_expr_location (const_tree);
|
||||
|
||||
extern int
|
||||
is_empty_class (tree type);
|
||||
|
||||
extern tree array_type_nelts_top (tree);
|
||||
|
||||
extern bool
|
||||
is_really_empty_class (tree, bool);
|
||||
|
||||
extern bool builtin_valid_in_constant_expr_p (const_tree);
|
||||
|
||||
extern bool maybe_constexpr_fn (tree);
|
||||
|
||||
extern bool var_in_maybe_constexpr_fn (tree);
|
||||
|
||||
extern int
|
||||
rs_type_quals (const_tree type);
|
||||
|
||||
extern bool decl_maybe_constant_var_p (tree);
|
||||
|
||||
extern tree
|
||||
rs_walk_subtrees (tree *, int *, walk_tree_fn, void *, hash_set<tree> *);
|
||||
#define rs_walk_tree(tp, func, data, pset) \
|
||||
walk_tree_1 (tp, func, data, pset, rs_walk_subtrees)
|
||||
#define rs_walk_tree_without_duplicates(tp, func, data) \
|
||||
walk_tree_without_duplicates_1 (tp, func, data, rs_walk_subtrees)
|
||||
|
||||
// forked from gcc/cp/cp-tree.h cp_expr_loc_or_loc
|
||||
|
||||
inline location_t
|
||||
rs_expr_loc_or_loc (const_tree t, location_t or_loc)
|
||||
{
|
||||
location_t loc = rs_expr_location (t);
|
||||
if (loc == UNKNOWN_LOCATION)
|
||||
loc = or_loc;
|
||||
return loc;
|
||||
}
|
||||
|
||||
// forked from gcc/cp/cp-tree.h cp_expr_loc_or_input_loc
|
||||
|
||||
inline location_t
|
||||
rs_expr_loc_or_input_loc (const_tree t)
|
||||
{
|
||||
return rs_expr_loc_or_loc (t, input_location);
|
||||
}
|
||||
|
||||
} // namespace Rust
|
||||
|
||||
#endif // RUST_TREE
|
||||
506
gcc/rust/rust-backend.h
Normal file
506
gcc/rust/rust-backend.h
Normal file
@@ -0,0 +1,506 @@
|
||||
// Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
||||
|
||||
// This file is part of GCC.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT 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
|
||||
// along with GCC; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef RUST_BACKEND_H
|
||||
#define RUST_BACKEND_H
|
||||
|
||||
#include <gmp.h>
|
||||
#include <mpfr.h>
|
||||
#include <mpc.h>
|
||||
|
||||
#include "rust-location.h"
|
||||
#include "rust-linemap.h"
|
||||
#include "rust-diagnostics.h"
|
||||
#include "operator.h"
|
||||
#include "tree.h"
|
||||
|
||||
// Pointers to these types are created by the backend, passed to the
|
||||
// frontend, and passed back to the backend. The types must be
|
||||
// defined by the backend using these names.
|
||||
|
||||
// The backend representation of a variable.
|
||||
class Bvariable;
|
||||
|
||||
// The backend interface. This is a pure abstract class that a
|
||||
// specific backend will implement.
|
||||
|
||||
class Backend
|
||||
{
|
||||
public:
|
||||
virtual ~Backend () {}
|
||||
|
||||
// Name/type/location. Used for function parameters, struct fields,
|
||||
// interface methods.
|
||||
struct typed_identifier
|
||||
{
|
||||
std::string name;
|
||||
tree type;
|
||||
Location location;
|
||||
|
||||
typed_identifier ()
|
||||
: name (), type (NULL_TREE), location (Linemap::unknown_location ())
|
||||
{}
|
||||
|
||||
typed_identifier (const std::string &a_name, tree a_type,
|
||||
Location a_location)
|
||||
: name (a_name), type (a_type), location (a_location)
|
||||
{}
|
||||
};
|
||||
|
||||
// debug
|
||||
virtual void debug (tree) = 0;
|
||||
virtual void debug (Bvariable *) = 0;
|
||||
|
||||
virtual tree get_identifier_node (const std::string &str) = 0;
|
||||
|
||||
// Types.
|
||||
|
||||
// get unit-type
|
||||
virtual tree unit_type () = 0;
|
||||
|
||||
// Get the unnamed boolean type.
|
||||
virtual tree bool_type () = 0;
|
||||
|
||||
// Get the char type
|
||||
virtual tree char_type () = 0;
|
||||
|
||||
// Get the wchar type
|
||||
virtual tree wchar_type () = 0;
|
||||
|
||||
// Get the Host pointer size in bits
|
||||
virtual int get_pointer_size () = 0;
|
||||
|
||||
// Get the raw str type const char*
|
||||
virtual tree raw_str_type () = 0;
|
||||
|
||||
// Get an unnamed integer type with the given signedness and number
|
||||
// of bits.
|
||||
virtual tree integer_type (bool is_unsigned, int bits) = 0;
|
||||
|
||||
// Get an unnamed floating point type with the given number of bits
|
||||
// (32 or 64).
|
||||
virtual tree float_type (int bits) = 0;
|
||||
|
||||
// Get an unnamed complex type with the given number of bits (64 or 128).
|
||||
virtual tree complex_type (int bits) = 0;
|
||||
|
||||
// Get a pointer type.
|
||||
virtual tree pointer_type (tree to_type) = 0;
|
||||
|
||||
// Get a reference type.
|
||||
virtual tree reference_type (tree to_type) = 0;
|
||||
|
||||
// make type immutable
|
||||
virtual tree immutable_type (tree base) = 0;
|
||||
|
||||
// Get a function type. The receiver, parameter, and results are
|
||||
// generated from the types in the Function_type. The Function_type
|
||||
// is provided so that the names are available. This should return
|
||||
// not the type of a Go function (which is a pointer to a struct)
|
||||
// but the type of a C function pointer (which will be used as the
|
||||
// type of the first field of the struct). If there is more than
|
||||
// one result, RESULT_STRUCT is a struct type to hold the results,
|
||||
// and RESULTS may be ignored; if there are zero or one results,
|
||||
// RESULT_STRUCT is NULL.
|
||||
virtual tree function_type (const typed_identifier &receiver,
|
||||
const std::vector<typed_identifier> ¶meters,
|
||||
const std::vector<typed_identifier> &results,
|
||||
tree result_struct, Location location)
|
||||
= 0;
|
||||
|
||||
virtual tree
|
||||
function_type_varadic (const typed_identifier &receiver,
|
||||
const std::vector<typed_identifier> ¶meters,
|
||||
const std::vector<typed_identifier> &results,
|
||||
tree result_struct, Location location)
|
||||
= 0;
|
||||
|
||||
virtual tree function_ptr_type (tree result,
|
||||
const std::vector<tree> &praameters,
|
||||
Location location)
|
||||
= 0;
|
||||
|
||||
// Get a struct type.
|
||||
virtual tree struct_type (const std::vector<typed_identifier> &fields) = 0;
|
||||
|
||||
// Get a union type.
|
||||
virtual tree union_type (const std::vector<typed_identifier> &fields) = 0;
|
||||
|
||||
// Get an array type.
|
||||
virtual tree array_type (tree element_type, tree length) = 0;
|
||||
|
||||
// Return a named version of a type. The location is the location
|
||||
// of the type definition. This will not be called for a type
|
||||
// created via placeholder_pointer_type, placeholder_struct_type, or
|
||||
// placeholder_array_type.. (It may be called for a pointer,
|
||||
// struct, or array type in a case like "type P *byte; type Q P".)
|
||||
virtual tree named_type (const std::string &name, tree, Location) = 0;
|
||||
|
||||
// Return the size of a type.
|
||||
virtual int64_t type_size (tree) = 0;
|
||||
|
||||
// Return the alignment of a type.
|
||||
virtual int64_t type_alignment (tree) = 0;
|
||||
|
||||
// Return the alignment of a struct field of this type. This is
|
||||
// normally the same as type_alignment, but not always.
|
||||
virtual int64_t type_field_alignment (tree) = 0;
|
||||
|
||||
// Return the offset of field INDEX in a struct type. INDEX is the
|
||||
// entry in the FIELDS std::vector parameter of struct_type or
|
||||
// set_placeholder_struct_type.
|
||||
virtual int64_t type_field_offset (tree, size_t index) = 0;
|
||||
|
||||
// Expressions.
|
||||
|
||||
// Return an expression for a zero value of the given type. This is
|
||||
// used for cases such as local variable initialization and
|
||||
// converting nil to other types.
|
||||
virtual tree zero_expression (tree) = 0;
|
||||
|
||||
virtual tree unit_expression () = 0;
|
||||
|
||||
// Create a reference to a variable.
|
||||
virtual tree var_expression (Bvariable *var, Location) = 0;
|
||||
|
||||
// Return an expression for the multi-precision integer VAL in BTYPE.
|
||||
virtual tree integer_constant_expression (tree btype, mpz_t val) = 0;
|
||||
|
||||
// Return an expression for the floating point value VAL in BTYPE.
|
||||
virtual tree float_constant_expression (tree btype, mpfr_t val) = 0;
|
||||
|
||||
// Return an expression for the complex value VAL in BTYPE.
|
||||
virtual tree complex_constant_expression (tree btype, mpc_t val) = 0;
|
||||
|
||||
// Return an expression for the string value VAL.
|
||||
virtual tree string_constant_expression (const std::string &val) = 0;
|
||||
|
||||
// Get a char literal
|
||||
virtual tree char_constant_expression (char c) = 0;
|
||||
|
||||
// Get a char literal
|
||||
virtual tree wchar_constant_expression (wchar_t c) = 0;
|
||||
|
||||
// Return an expression for the boolean value VAL.
|
||||
virtual tree boolean_constant_expression (bool val) = 0;
|
||||
|
||||
// Return an expression for the real part of BCOMPLEX.
|
||||
virtual tree real_part_expression (tree bcomplex, Location) = 0;
|
||||
|
||||
// Return an expression for the imaginary part of BCOMPLEX.
|
||||
virtual tree imag_part_expression (tree bcomplex, Location) = 0;
|
||||
|
||||
// Return an expression for the complex number (BREAL, BIMAG).
|
||||
virtual tree complex_expression (tree breal, tree bimag, Location) = 0;
|
||||
|
||||
// Return an expression that converts EXPR to TYPE.
|
||||
virtual tree convert_expression (tree type, tree expr, Location) = 0;
|
||||
|
||||
// Return an expression for the field at INDEX in BSTRUCT.
|
||||
virtual tree struct_field_expression (tree bstruct, size_t index, Location)
|
||||
= 0;
|
||||
|
||||
// Create an expression that executes BSTAT before BEXPR.
|
||||
virtual tree compound_expression (tree bstat, tree bexpr, Location) = 0;
|
||||
|
||||
// Return an expression that executes THEN_EXPR if CONDITION is true, or
|
||||
// ELSE_EXPR otherwise and returns the result as type BTYPE, within the
|
||||
// specified function FUNCTION. ELSE_EXPR may be NULL. BTYPE may be NULL.
|
||||
virtual tree conditional_expression (tree function, tree btype,
|
||||
tree condition, tree then_expr,
|
||||
tree else_expr, Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression for the negation operation OP EXPR.
|
||||
// Supported values of OP are enumerated in NegationOperator.
|
||||
virtual tree negation_expression (NegationOperator op, tree expr, Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression for the operation LEFT OP RIGHT.
|
||||
// Supported values of OP are enumerated in ArithmeticOrLogicalOperator.
|
||||
virtual tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
|
||||
tree left, tree right,
|
||||
Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression for the operation LEFT OP RIGHT.
|
||||
// Supported values of OP are enumerated in ComparisonOperator.
|
||||
virtual tree comparison_expression (ComparisonOperator op, tree left,
|
||||
tree right, Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression for the operation LEFT OP RIGHT.
|
||||
// Supported values of OP are enumerated in LazyBooleanOperator.
|
||||
virtual tree lazy_boolean_expression (LazyBooleanOperator op, tree left,
|
||||
tree right, Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression that constructs BTYPE with VALS. BTYPE must be the
|
||||
// backend representation a of struct. VALS must be in the same order as the
|
||||
// corresponding fields in BTYPE.
|
||||
virtual tree constructor_expression (tree btype, bool is_variant,
|
||||
const std::vector<tree> &vals, int,
|
||||
Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression that constructs an array of BTYPE with INDEXES and
|
||||
// VALS. INDEXES and VALS must have the same amount of elements. Each index
|
||||
// in INDEXES must be in the same order as the corresponding value in VALS.
|
||||
virtual tree
|
||||
array_constructor_expression (tree btype,
|
||||
const std::vector<unsigned long> &indexes,
|
||||
const std::vector<tree> &vals, Location)
|
||||
= 0;
|
||||
|
||||
virtual tree array_initializer (tree, tree, tree, tree, tree, tree *,
|
||||
Location)
|
||||
= 0;
|
||||
|
||||
// Return an expression for ARRAY[INDEX] as an l-value. ARRAY is a valid
|
||||
// fixed-length array, not a slice.
|
||||
virtual tree array_index_expression (tree array, tree index, Location) = 0;
|
||||
|
||||
// Create an expression for a call to FN with ARGS, taking place within
|
||||
// caller CALLER.
|
||||
virtual tree call_expression (tree fn, const std::vector<tree> &args,
|
||||
tree static_chain, Location)
|
||||
= 0;
|
||||
|
||||
// Statements.
|
||||
|
||||
// Create a variable initialization statement in the specified
|
||||
// function. This initializes a local variable at the point in the
|
||||
// program flow where it is declared.
|
||||
virtual tree init_statement (tree, Bvariable *var, tree init) = 0;
|
||||
|
||||
// Create an assignment statement within the specified function.
|
||||
virtual tree assignment_statement (tree lhs, tree rhs, Location) = 0;
|
||||
|
||||
// Create a return statement, passing the representation of the
|
||||
// function and the list of values to return.
|
||||
virtual tree return_statement (tree, const std::vector<tree> &, Location) = 0;
|
||||
|
||||
// Create an if statement within a function. ELSE_BLOCK may be NULL.
|
||||
virtual tree if_statement (tree, tree condition, tree then_block,
|
||||
tree else_block, Location)
|
||||
= 0;
|
||||
|
||||
// infinite loop expressions
|
||||
virtual tree loop_expression (tree body, Location) = 0;
|
||||
|
||||
// exit expressions
|
||||
virtual tree exit_expression (tree condition, Location) = 0;
|
||||
|
||||
// Create a single statement from two statements.
|
||||
virtual tree compound_statement (tree, tree) = 0;
|
||||
|
||||
// Create a single statement from a list of statements.
|
||||
virtual tree statement_list (const std::vector<tree> &) = 0;
|
||||
|
||||
// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if
|
||||
// an exception occurs. EXCEPT_STMT may be NULL. FINALLY_STMT may be NULL and
|
||||
// if not NULL, it will always be executed. This is used for handling defers
|
||||
// in Go functions. In C++, the resulting code is of this form:
|
||||
// try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
|
||||
virtual tree exception_handler_statement (tree bstat, tree except_stmt,
|
||||
tree finally_stmt, Location)
|
||||
= 0;
|
||||
|
||||
// Blocks.
|
||||
|
||||
// Create a block. The frontend will call this function when it
|
||||
// starts converting a block within a function. FUNCTION is the
|
||||
// current function. ENCLOSING is the enclosing block; it will be
|
||||
// NULL for the top-level block in a function. VARS is the list of
|
||||
// local variables defined within this block; each entry will be
|
||||
// created by the local_variable function. START_LOCATION is the
|
||||
// location of the start of the block, more or less the location of
|
||||
// the initial curly brace. END_LOCATION is the location of the end
|
||||
// of the block, more or less the location of the final curly brace.
|
||||
// The statements will be added after the block is created.
|
||||
virtual tree block (tree function, tree enclosing,
|
||||
const std::vector<Bvariable *> &vars,
|
||||
Location start_location, Location end_location)
|
||||
= 0;
|
||||
|
||||
// Add the statements to a block. The block is created first. Then
|
||||
// the statements are created. Then the statements are added to the
|
||||
// block. This will called exactly once per block. The vector may
|
||||
// be empty if there are no statements.
|
||||
virtual void block_add_statements (tree, const std::vector<tree> &) = 0;
|
||||
|
||||
// Variables.
|
||||
|
||||
// Create an error variable. This is used for cases which should
|
||||
// not occur in a correct program, in order to keep the compilation
|
||||
// going without crashing.
|
||||
virtual Bvariable *error_variable () = 0;
|
||||
|
||||
// Create a global variable. NAME is the package-qualified name of
|
||||
// the variable. ASM_NAME is the encoded identifier for the
|
||||
// variable, incorporating the package, and made safe for the
|
||||
// assembler. BTYPE is the type of the variable. IS_EXTERNAL is
|
||||
// true if the variable is defined in some other package. IS_HIDDEN
|
||||
// is true if the variable is not exported (name begins with a lower
|
||||
// case letter). IN_UNIQUE_SECTION is true if the variable should
|
||||
// be put into a unique section if possible; this is intended to
|
||||
// permit the linker to garbage collect the variable if it is not
|
||||
// referenced. LOCATION is where the variable was defined.
|
||||
virtual Bvariable *global_variable (const std::string &name,
|
||||
const std::string &asm_name, tree btype,
|
||||
bool is_external, bool is_hidden,
|
||||
bool in_unique_section, Location location)
|
||||
= 0;
|
||||
|
||||
// A global variable will 1) be initialized to zero, or 2) be
|
||||
// initialized to a constant value, or 3) be initialized in the init
|
||||
// function. In case 2, the frontend will call
|
||||
// global_variable_set_init to set the initial value. If this is
|
||||
// not called, the backend should initialize a global variable to 0.
|
||||
// The init function may then assign a value to it.
|
||||
virtual void global_variable_set_init (Bvariable *, tree) = 0;
|
||||
|
||||
// Create a local variable. The frontend will create the local
|
||||
// variables first, and then create the block which contains them.
|
||||
// FUNCTION is the function in which the variable is defined. NAME
|
||||
// is the name of the variable. TYPE is the type. DECL_VAR, if not
|
||||
// null, gives the location at which the value of this variable may
|
||||
// be found, typically used to create an inner-scope reference to an
|
||||
// outer-scope variable, to extend the lifetime of the variable beyond
|
||||
// the inner scope. IS_ADDRESS_TAKEN is true if the address of this
|
||||
// variable is taken (this implies that the address does not escape
|
||||
// the function, as otherwise the variable would be on the heap).
|
||||
// LOCATION is where the variable is defined. For each local variable
|
||||
// the frontend will call init_statement to set the initial value.
|
||||
virtual Bvariable *local_variable (tree function, const std::string &name,
|
||||
tree type, Bvariable *decl_var,
|
||||
Location location)
|
||||
= 0;
|
||||
|
||||
// Create a function parameter. This is an incoming parameter, not
|
||||
// a result parameter (result parameters are treated as local
|
||||
// variables). The arguments are as for local_variable.
|
||||
virtual Bvariable *parameter_variable (tree function, const std::string &name,
|
||||
tree type, Location location)
|
||||
= 0;
|
||||
|
||||
// Create a static chain parameter. This is the closure parameter.
|
||||
virtual Bvariable *static_chain_variable (tree function,
|
||||
const std::string &name, tree type,
|
||||
Location location)
|
||||
= 0;
|
||||
|
||||
// Create a temporary variable. A temporary variable has no name,
|
||||
// just a type. We pass in FUNCTION and BLOCK in case they are
|
||||
// needed. If INIT is not NULL, the variable should be initialized
|
||||
// to that value. Otherwise the initial value is irrelevant--the
|
||||
// backend does not have to explicitly initialize it to zero.
|
||||
// ADDRESS_IS_TAKEN is true if the programs needs to take the
|
||||
// address of this temporary variable. LOCATION is the location of
|
||||
// the statement or expression which requires creating the temporary
|
||||
// variable, and may not be very useful. This function should
|
||||
// return a variable which can be referenced later and should set
|
||||
// *PSTATEMENT to a statement which initializes the variable.
|
||||
virtual Bvariable *temporary_variable (tree, tree, tree, tree init,
|
||||
bool address_is_taken,
|
||||
Location location, tree *pstatement)
|
||||
= 0;
|
||||
|
||||
// Labels.
|
||||
|
||||
// Create a new label. NAME will be empty if this is a label
|
||||
// created by the frontend for a loop construct. The location is
|
||||
// where the label is defined.
|
||||
virtual tree label (tree, const std::string &name, Location) = 0;
|
||||
|
||||
// Create a statement which defines a label. This statement will be
|
||||
// put into the codestream at the point where the label should be
|
||||
// defined.
|
||||
virtual tree label_definition_statement (tree) = 0;
|
||||
|
||||
// Create a goto statement to a label.
|
||||
virtual tree goto_statement (tree, Location) = 0;
|
||||
|
||||
// Create an expression for the address of a label. This is used to
|
||||
// get the return address of a deferred function which may call
|
||||
// recover.
|
||||
virtual tree label_address (tree, Location) = 0;
|
||||
|
||||
// Functions.
|
||||
|
||||
// Bit flags to pass to the function method.
|
||||
|
||||
// Set if this is a function declaration rather than a definition;
|
||||
// the definition will be in another compilation unit.
|
||||
static const unsigned int function_is_declaration = 1 << 0;
|
||||
|
||||
// Set if the function should never be inlined because they call
|
||||
// recover and must be visible for correct panic recovery.
|
||||
static const unsigned int function_is_uninlinable = 1 << 1;
|
||||
|
||||
// Set if the function does not return. This is set for the
|
||||
// implementation of panic.
|
||||
static const unsigned int function_does_not_return = 1 << 2;
|
||||
|
||||
// Set if the function should be put in a unique section if
|
||||
// possible. This is used for field tracking.
|
||||
static const unsigned int function_in_unique_section = 1 << 3;
|
||||
|
||||
// Declare or define a function of FNTYPE.
|
||||
// NAME is the Go name of the function. ASM_NAME, if not the empty
|
||||
// string, is the name that should be used in the symbol table; this
|
||||
// will be non-empty if a magic extern comment is used. FLAGS is
|
||||
// bit flags described above.
|
||||
virtual tree function (tree fntype, const std::string &name,
|
||||
const std::string &asm_name, unsigned int flags,
|
||||
Location)
|
||||
= 0;
|
||||
|
||||
// Create a statement that runs all deferred calls for FUNCTION. This should
|
||||
// be a statement that looks like this in C++:
|
||||
// finish:
|
||||
// try { DEFER_RETURN; } catch { CHECK_DEFER; goto finish; }
|
||||
virtual tree function_defer_statement (tree function, tree undefer,
|
||||
tree check_defer, Location)
|
||||
= 0;
|
||||
|
||||
// Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
|
||||
// This will only be called for a function definition. Returns true on
|
||||
// success, false on failure.
|
||||
virtual bool
|
||||
function_set_parameters (tree function,
|
||||
const std::vector<Bvariable *> ¶m_vars)
|
||||
= 0;
|
||||
|
||||
// Utility.
|
||||
|
||||
// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
|
||||
// FUNCTION_DECLS, and VARIABLE_DECLS declared globally.
|
||||
virtual void
|
||||
write_global_definitions (const std::vector<tree> &type_decls,
|
||||
const std::vector<tree> &constant_decls,
|
||||
const std::vector<tree> &function_decls,
|
||||
const std::vector<Bvariable *> &variable_decls)
|
||||
= 0;
|
||||
|
||||
// Write SIZE bytes of export data from BYTES to the proper
|
||||
// section in the output object file.
|
||||
virtual void write_export_data (const char *bytes, unsigned int size) = 0;
|
||||
};
|
||||
|
||||
#endif // RUST_BACKEND_H
|
||||
2718
gcc/rust/rust-gcc.cc
Normal file
2718
gcc/rust/rust-gcc.cc
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user