From patchwork Tue Dec 6 10:14:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Cohen X-Patchwork-Id: 61537 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4EE75392B158 for ; Tue, 6 Dec 2022 10:26:38 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) by sourceware.org (Postfix) with ESMTPS id F12EE3AA8C09 for ; Tue, 6 Dec 2022 10:13:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org F12EE3AA8C09 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wr1-x42c.google.com with SMTP id y16so22762481wrm.2 for ; Tue, 06 Dec 2022 02:13:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=IsSTYrIAkXZWcYb1qUf4AKFPjE6wbiv/x0V4YEhh30o=; b=eRqjKmUl2RHktY25/YZMgh+6DlqPK4t7uOrW/E1iLOWq5FsE2BqP/yT3OU1zmAcqGQ jcQ4tpvoBXkaNTC7gLD5ph6QNBRWV8/6KLVjlQQ5DLVrXCQNhgB2/NgElh9CdaVfd5aB 9Imt1J0vBOuNh2DZqg0UdIUqmhsHW5ggy2NZDK2+N4kLdKOVHuv+trzPCTcgI7yIw54d b/eLfCCe1Df5rR13ptP+OesbTGTzpfcasOrl8k5yeP5b41E6yvowFt58TYAtA6MgPPeH wYsd2NVD9QYz4xwqbPwW/Icl7ydRXupQkfhr0oc7QNglrFl8B5OvOaiit5VMDyiFIe8k ZZfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=IsSTYrIAkXZWcYb1qUf4AKFPjE6wbiv/x0V4YEhh30o=; b=dR+XM68BMlQY9cz7+DxXWsRjGHWpCvzJrmqvwAJsjgXDI137KK501IV3+SVNb6F6hb WFdnJVadbK5xqkoA30RA943LOgpV5Q5u5cckyX/8ubcxgmviePJ4dkknPvR0DQJtUQTp lk1tNHNEenff9FzrAs5pvKYTOvJXMEAHo2nK01KsTyp8ZH1AabLFfveuf9wqn5YPIvQB b8KLFoh19LBrlxKa8BEx21ZJKEFaLQilQLXjTMNil6XJKMhGDyQvwEGnNLAm8gOQYMsz RvdCWyjCsBti4DgrPAMI/k2JNvaG41HIufibX38dV0Wdh6WjyQk6yRDlyj2ht6QdTmKe h7DQ== X-Gm-Message-State: ANoB5plZGGUF6hShcrVAqKW3rZitaKqSHOiYasiXVrlEwAvJMYSPr7yQ NwJ+e2Zv7sweOcznS7k1wyAG2T9oneeptIr2TA== X-Google-Smtp-Source: AA0mqf5iTJah2Psv1HYjSZTz8QZSAMo4YwyVsOFhjQGfkH3nJ/uy3UE/BzUNgUrC2y5I4ChUGzJZOg== X-Received: by 2002:adf:ea44:0:b0:242:19b2:f1fc with SMTP id j4-20020adfea44000000b0024219b2f1fcmr24355577wrn.593.1670321606848; Tue, 06 Dec 2022 02:13:26 -0800 (PST) Received: from platypus.lan ([2001:861:5e4c:3bb0:6424:328a:1734:3249]) by smtp.googlemail.com with ESMTPSA id r10-20020a05600c458a00b003cfd4a50d5asm27052699wmo.34.2022.12.06.02.13.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Dec 2022 02:13:26 -0800 (PST) From: arthur.cohen@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, richard.guenther@gmail.com, jakub@redhat.com, Philip Herron , David Faust Subject: [PATCH Rust front-end v4 36/46] gccrs: Add base for HIR to GCC GENERIC lowering Date: Tue, 6 Dec 2022 11:14:11 +0100 Message-Id: <20221206101417.778807-37-arthur.cohen@embecosm.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221206101417.778807-1-arthur.cohen@embecosm.com> References: <20221206101417.778807-1-arthur.cohen@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-17.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: arthur.cohen@embecosm.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" From: Philip Herron 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. Co-authored-by: David Faust --- gcc/rust/backend/rust-builtins.h | 189 ++ gcc/rust/backend/rust-compile-base.cc | 730 +++++++ gcc/rust/backend/rust-compile-base.h | 146 ++ gcc/rust/backend/rust-mangle.cc | 307 +++ gcc/rust/backend/rust-mangle.h | 52 + gcc/rust/backend/rust-tree.cc | 958 +++++++++ gcc/rust/backend/rust-tree.h | 508 +++++ gcc/rust/rust-backend.h | 506 +++++ gcc/rust/rust-gcc.cc | 2718 +++++++++++++++++++++++++ 9 files changed, 6114 insertions(+) create mode 100644 gcc/rust/backend/rust-builtins.h create mode 100644 gcc/rust/backend/rust-compile-base.cc create mode 100644 gcc/rust/backend/rust-compile-base.h create mode 100644 gcc/rust/backend/rust-mangle.cc create mode 100644 gcc/rust/backend/rust-mangle.h create mode 100644 gcc/rust/backend/rust-tree.cc create mode 100644 gcc/rust/backend/rust-tree.h create mode 100644 gcc/rust/rust-backend.h create mode 100644 gcc/rust/rust-gcc.cc diff --git a/gcc/rust/backend/rust-builtins.h b/gcc/rust/backend/rust-builtins.h new file mode 100644 index 00000000000..2bfa6c6cdf7 --- /dev/null +++ b/gcc/rust/backend/rust-builtins.h @@ -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 +// . + +#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 builtin_functions_; + std::map rust_intrinsic_to_gcc_builtin; +}; + +} // namespace Compile +} // namespace Rust + +#endif // RUST_BUILTINS_H diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc new file mode 100644 index 00000000000..2b5c850872f --- /dev/null +++ b/gcc/rust/backend/rust-compile-base.cc @@ -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 +// . + +#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 % 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 (), + "% expects exactly one argment"); + return; + } + + rust_assert (attr.get_attr_input ().get_attr_input_type () + == AST::AttrInput::AttrInputType::LITERAL); + + auto &literal = static_cast (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 % 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 (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 (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 % 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 (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 (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 +HIRCompileBase::compile_locals_for_block (Context *ctx, Resolver::Rib &rib, + tree fndecl) +{ + std::vector 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 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 &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 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 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 (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 diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h new file mode 100644 index 00000000000..4c20933cafc --- /dev/null +++ b/gcc/rust/backend/rust-compile-base.h @@ -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 +// . + +#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> + &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 &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 + 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 &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 diff --git a/gcc/rust/backend/rust-mangle.cc b/gcc/rust/backend/rust-mangle.cc new file mode 100644 index 00000000000..4d202078a70 --- /dev/null +++ b/gcc/rust/backend/rust-mangle.cc @@ -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 + // + // ::fooA: + // _ZN43_$LT$example..Bar$u20$as$u20$example..A$GT$4fooA17hfc615fa76c7db7a0E: + // + // core::ptr::const_ptr::::cast: + // _ZN4core3ptr9const_ptr33_$LT$impl$u20$$BP$const$u20$T$GT$4cast17hb79f4617226f1d55E: + // + // core::ptr::const_ptr::::as_ptr: + // _ZN4core3ptr9const_ptr43_$LT$impl$u20$$BP$const$u20$$u5b$T$u5d$$GT$6as_ptr17he16e0dcd9473b04fE: + // + // example::Foo::new: + // _ZN7example12Foo$LT$T$GT$3new17h9a2aacb7fd783515E: + // + // >::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 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 `` 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 + v0_add_integer_62(itself), that is +// + 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 +// `` 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 , right under the + // 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 diff --git a/gcc/rust/backend/rust-mangle.h b/gcc/rust/backend/rust-mangle.h new file mode 100644 index 00000000000..6d5a64f8bce --- /dev/null +++ b/gcc/rust/backend/rust-mangle.h @@ -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 +// . + +#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 (frust_mangling_value); + } + +private: + static enum MangleVersion version; +}; + +} // namespace Compile +} // namespace Rust + +#endif // RUST_MANGLE_H diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc new file mode 100644 index 00000000000..3d71e19fe82 --- /dev/null +++ b/gcc/rust/backend/rust-tree.cc @@ -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 +// . + +#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 *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 diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h new file mode 100644 index 00000000000..a667cbfc8ad --- /dev/null +++ b/gcc/rust/backend/rust-tree.h @@ -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 +// . + +#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 *vcall_indices; + tree vtables; + tree typeinfo_var; + vec *vbases; + tree as_base; + vec *pure_virtuals; + tree friend_classes; + vec *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 *); +#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 diff --git a/gcc/rust/rust-backend.h b/gcc/rust/rust-backend.h new file mode 100644 index 00000000000..126283c1a54 --- /dev/null +++ b/gcc/rust/rust-backend.h @@ -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 +// . + +#ifndef RUST_BACKEND_H +#define RUST_BACKEND_H + +#include +#include +#include + +#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 ¶meters, + const std::vector &results, + tree result_struct, Location location) + = 0; + + virtual tree + function_type_varadic (const typed_identifier &receiver, + const std::vector ¶meters, + const std::vector &results, + tree result_struct, Location location) + = 0; + + virtual tree function_ptr_type (tree result, + const std::vector &praameters, + Location location) + = 0; + + // Get a struct type. + virtual tree struct_type (const std::vector &fields) = 0; + + // Get a union type. + virtual tree union_type (const std::vector &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 &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 &indexes, + const std::vector &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 &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 &, 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 &) = 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 &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 &) = 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 ¶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 &type_decls, + const std::vector &constant_decls, + const std::vector &function_decls, + const std::vector &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 diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc new file mode 100644 index 00000000000..3a682fc0127 --- /dev/null +++ b/gcc/rust/rust-gcc.cc @@ -0,0 +1,2718 @@ +// rust-gcc.cc -- Rust frontend to gcc IR. +// Copyright (C) 2011-2022 Free Software Foundation, Inc. +// Contributed by Ian Lance Taylor, Google. +// forked from gccgo + +// 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 +// . + +#include "rust-system.h" + +// This has to be included outside of extern "C", so we have to +// include it here before tree.h includes it later. +#include + +#include "tree.h" +#include "opts.h" +#include "fold-const.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "varasm.h" +#include "tree-iterator.h" +#include "tm.h" +#include "function.h" +#include "cgraph.h" +#include "convert.h" +#include "gimple-expr.h" +#include "gimplify.h" +#include "langhooks.h" +#include "toplev.h" +#include "output.h" +#include "realmpfr.h" +#include "builtins.h" +#include "print-tree.h" +#include "attribs.h" + +#include "rust-location.h" +#include "rust-linemap.h" +#include "rust-backend.h" +#include "rust-object-export.h" + +#include "backend/rust-tree.h" + +// TODO: this will have to be significantly modified to work with Rust + +// Bvariable is a bit more complicated, because of zero-sized types. +// The GNU linker does not permit dynamic variables with zero size. +// When we see such a variable, we generate a version of the type with +// non-zero size. However, when referring to the global variable, we +// want an expression of zero size; otherwise, if, say, the global +// variable is passed to a function, we will be passing a +// non-zero-sized value to a zero-sized value, which can lead to a +// miscompilation. + +class Bvariable +{ +public: + Bvariable (tree t) : t_ (t), orig_type_ (NULL) {} + + Bvariable (tree t, tree orig_type) : t_ (t), orig_type_ (orig_type) {} + + // Get the tree for use as an expression. + tree get_tree (Location) const; + + // Get the actual decl; + tree get_decl () const { return this->t_; } + +private: + tree t_; + tree orig_type_; +}; + +// Get the tree of a variable for use as an expression. If this is a +// zero-sized global, create an expression that refers to the decl but +// has zero size. +tree +Bvariable::get_tree (Location location) const +{ + if (this->t_ == error_mark_node) + return error_mark_node; + + TREE_USED (this->t_) = 1; + if (this->orig_type_ == NULL || TREE_TYPE (this->t_) == this->orig_type_) + { + return this->t_; + } + + // Return *(orig_type*)&decl. */ + tree t = build_fold_addr_expr_loc (location.gcc_location (), this->t_); + t = fold_build1_loc (location.gcc_location (), NOP_EXPR, + build_pointer_type (this->orig_type_), t); + return build_fold_indirect_ref_loc (location.gcc_location (), t); +} + +// This file implements the interface between the Rust frontend proper +// and the gcc IR. This implements specific instantiations of +// abstract classes defined by the Rust frontend proper. The Rust +// frontend proper class methods of these classes to generate the +// backend representation. + +class Gcc_backend : public Backend +{ +public: + Gcc_backend (); + + void debug (tree t) { debug_tree (t); }; + void debug (Bvariable *t) { debug_tree (t->get_decl ()); }; + + tree get_identifier_node (const std::string &str) + { + return get_identifier_with_length (str.data (), str.length ()); + } + + // Types. + + tree unit_type () + { + static tree unit_type; + if (unit_type == nullptr) + { + auto unit_type_node = struct_type ({}); + unit_type = named_type ("()", unit_type_node, + ::Linemap::predeclared_location ()); + } + + return unit_type; + } + + tree bool_type () { return boolean_type_node; } + + tree char_type () { return char_type_node; } + + tree wchar_type () + { + tree wchar = make_unsigned_type (32); + TYPE_STRING_FLAG (wchar) = 1; + return wchar; + } + + int get_pointer_size (); + + tree raw_str_type (); + + tree integer_type (bool, int); + + tree float_type (int); + + tree complex_type (int); + + tree pointer_type (tree); + + tree reference_type (tree); + + tree immutable_type (tree); + + tree function_type (const typed_identifier &, + const std::vector &, + const std::vector &, tree, + const Location); + + tree function_type_varadic (const typed_identifier &, + const std::vector &, + const std::vector &, tree, + const Location); + + tree function_ptr_type (tree, const std::vector &, Location); + + tree struct_type (const std::vector &); + + tree union_type (const std::vector &); + + tree array_type (tree, tree); + + tree named_type (const std::string &, tree, Location); + + int64_t type_size (tree); + + int64_t type_alignment (tree); + + int64_t type_field_alignment (tree); + + int64_t type_field_offset (tree, size_t index); + + // Expressions. + + tree zero_expression (tree); + + tree unit_expression () { return integer_zero_node; } + + tree var_expression (Bvariable *var, Location); + + tree integer_constant_expression (tree type, mpz_t val); + + tree float_constant_expression (tree type, mpfr_t val); + + tree complex_constant_expression (tree type, mpc_t val); + + tree string_constant_expression (const std::string &val); + + tree wchar_constant_expression (wchar_t c); + + tree char_constant_expression (char c); + + tree boolean_constant_expression (bool val); + + tree real_part_expression (tree bcomplex, Location); + + tree imag_part_expression (tree bcomplex, Location); + + tree complex_expression (tree breal, tree bimag, Location); + + tree convert_expression (tree type, tree expr, Location); + + tree struct_field_expression (tree, size_t, Location); + + tree compound_expression (tree, tree, Location); + + tree conditional_expression (tree, tree, tree, tree, tree, Location); + + tree negation_expression (NegationOperator op, tree expr, Location); + + tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op, + tree left, tree right, Location); + + tree comparison_expression (ComparisonOperator op, tree left, tree right, + Location); + + tree lazy_boolean_expression (LazyBooleanOperator op, tree left, tree right, + Location); + + tree constructor_expression (tree, bool, const std::vector &, int, + Location); + + tree array_constructor_expression (tree, const std::vector &, + const std::vector &, Location); + + tree array_initializer (tree, tree, tree, tree, tree, tree *, Location); + + tree array_index_expression (tree array, tree index, Location); + + tree call_expression (tree fn, const std::vector &args, + tree static_chain, Location); + + // Statements. + + tree init_statement (tree, Bvariable *var, tree init); + + tree assignment_statement (tree lhs, tree rhs, Location); + + tree return_statement (tree, const std::vector &, Location); + + tree if_statement (tree, tree condition, tree then_block, tree else_block, + Location); + + tree compound_statement (tree, tree); + + tree statement_list (const std::vector &); + + tree exception_handler_statement (tree bstat, tree except_stmt, + tree finally_stmt, Location); + + tree loop_expression (tree body, Location); + + tree exit_expression (tree condition, Location); + + // Blocks. + + tree block (tree, tree, const std::vector &, Location, Location); + + void block_add_statements (tree, const std::vector &); + + // Variables. + + Bvariable *error_variable () { return new Bvariable (error_mark_node); } + + Bvariable *global_variable (const std::string &var_name, + const std::string &asm_name, tree type, + bool is_external, bool is_hidden, + bool in_unique_section, Location location); + + void global_variable_set_init (Bvariable *, tree); + + Bvariable *local_variable (tree, const std::string &, tree, Bvariable *, + Location); + + Bvariable *parameter_variable (tree, const std::string &, tree, Location); + + Bvariable *static_chain_variable (tree, const std::string &, tree, Location); + + Bvariable *temporary_variable (tree, tree, tree, tree, bool, Location, + tree *); + + // Labels. + + tree label (tree, const std::string &name, Location); + + tree label_definition_statement (tree); + + tree goto_statement (tree, Location); + + tree label_address (tree, Location); + + // Functions. + + tree function (tree fntype, const std::string &name, + const std::string &asm_name, unsigned int flags, Location); + + tree function_defer_statement (tree function, tree undefer, tree defer, + Location); + + bool function_set_parameters (tree function, + const std::vector &); + + void write_global_definitions (const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &); + + void write_export_data (const char *bytes, unsigned int size); + +private: + tree fill_in_fields (tree, const std::vector &); + + tree fill_in_array (tree, tree, tree); + + tree non_zero_size_type (tree); + + tree convert_tree (tree, tree, Location); +}; + +// A helper function to create a GCC identifier from a C++ string. + +static inline tree +get_identifier_from_string (const std::string &str) +{ + return get_identifier_with_length (str.data (), str.length ()); +} + +// Define the built-in functions that are exposed to GCCRust. + +Gcc_backend::Gcc_backend () +{ + /* We need to define the fetch_and_add functions, since we use them + for ++ and --. */ + // tree t = this->integer_type (true, BITS_PER_UNIT)->get_tree (); + // tree p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_1, + // "__sync_fetch_and_add_1", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // t = this->integer_type (true, BITS_PER_UNIT * 2)->get_tree (); + // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_2, + // "__sync_fetch_and_add_2", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // t = this->integer_type (true, BITS_PER_UNIT * 4)->get_tree (); + // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_4, + // "__sync_fetch_and_add_4", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // t = this->integer_type (true, BITS_PER_UNIT * 8)->get_tree (); + // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_8, + // "__sync_fetch_and_add_8", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // // We use __builtin_expect for magic import functions. + // this->define_builtin (BUILT_IN_EXPECT, "__builtin_expect", NULL, + // build_function_type_list (long_integer_type_node, + // long_integer_type_node, + // long_integer_type_node, + // NULL_TREE), + // builtin_const); + + // // We use __builtin_memcmp for struct comparisons. + // this->define_builtin (BUILT_IN_MEMCMP, "__builtin_memcmp", "memcmp", + // build_function_type_list (integer_type_node, + // const_ptr_type_node, + // const_ptr_type_node, + // size_type_node, NULL_TREE), + // 0); + + // // We use __builtin_memmove for copying data. + // this->define_builtin (BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove", + // build_function_type_list (void_type_node, ptr_type_node, + // const_ptr_type_node, + // size_type_node, NULL_TREE), + // 0); + + // // We use __builtin_memset for zeroing data. + // this->define_builtin (BUILT_IN_MEMSET, "__builtin_memset", "memset", + // build_function_type_list (void_type_node, ptr_type_node, + // integer_type_node, + // size_type_node, NULL_TREE), + // 0); + + // // Used by runtime/internal/sys and math/bits. + // this->define_builtin (BUILT_IN_CTZ, "__builtin_ctz", "ctz", + // build_function_type_list (integer_type_node, + // unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_CTZLL, "__builtin_ctzll", "ctzll", + // build_function_type_list (integer_type_node, + // long_long_unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_CLZ, "__builtin_clz", "clz", + // build_function_type_list (integer_type_node, + // unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_CLZLL, "__builtin_clzll", "clzll", + // build_function_type_list (integer_type_node, + // long_long_unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_POPCOUNT, "__builtin_popcount", "popcount", + // build_function_type_list (integer_type_node, + // unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_POPCOUNTLL, "__builtin_popcountll", + // "popcountll", + // build_function_type_list (integer_type_node, + // long_long_unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_BSWAP16, "__builtin_bswap16", "bswap16", + // build_function_type_list (uint16_type_node, + // uint16_type_node, NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32", + // build_function_type_list (uint32_type_node, + // uint32_type_node, NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_BSWAP64, "__builtin_bswap64", "bswap64", + // build_function_type_list (uint64_type_node, + // uint64_type_node, NULL_TREE), + // builtin_const); + + // We provide some functions for the math library. + + // We use __builtin_return_address in the thunk we build for + // functions which call recover, and for runtime.getcallerpc. + // t = build_function_type_list (ptr_type_node, unsigned_type_node, + // NULL_TREE); this->define_builtin (BUILT_IN_RETURN_ADDRESS, + // "__builtin_return_address", + // NULL, t, 0); + + // The runtime calls __builtin_dwarf_cfa for runtime.getcallersp. + // t = build_function_type_list (ptr_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_DWARF_CFA, "__builtin_dwarf_cfa", NULL, t, + // 0); + + // The runtime calls __builtin_extract_return_addr when recording + // the address to which a function returns. + // this->define_builtin ( + // BUILT_IN_EXTRACT_RETURN_ADDR, "__builtin_extract_return_addr", NULL, + // build_function_type_list (ptr_type_node, ptr_type_node, NULL_TREE), 0); + + // The compiler uses __builtin_trap for some exception handling + // cases. + // this->define_builtin (BUILT_IN_TRAP, "__builtin_trap", NULL, + // build_function_type (void_type_node, void_list_node), + // builtin_noreturn); + + // The runtime uses __builtin_prefetch. + // this->define_builtin (BUILT_IN_PREFETCH, "__builtin_prefetch", NULL, + // build_varargs_function_type_list (void_type_node, + // const_ptr_type_node, + // NULL_TREE), + // builtin_novops); + + // The compiler uses __builtin_unreachable for cases that cannot + // occur. + // this->define_builtin (BUILT_IN_UNREACHABLE, "__builtin_unreachable", NULL, + // build_function_type (void_type_node, void_list_node), + // builtin_const | builtin_noreturn); + + // We provide some atomic functions. + // t = build_function_type_list (uint32_type_node, ptr_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_LOAD_4, "__atomic_load_4", NULL, t, + // 0); + + // t = build_function_type_list (uint64_type_node, ptr_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_LOAD_8, "__atomic_load_8", NULL, t, + // 0); + + // t = build_function_type_list (void_type_node, ptr_type_node, + // uint32_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_STORE_4, "__atomic_store_4", NULL, t, + // 0); + + // t = build_function_type_list (void_type_node, ptr_type_node, + // uint64_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_STORE_8, "__atomic_store_8", NULL, t, + // 0); + + // t = build_function_type_list (uint32_type_node, ptr_type_node, + // uint32_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_4, "__atomic_exchange_4", + // NULL, + // t, 0); + + // t = build_function_type_list (uint64_type_node, ptr_type_node, + // uint64_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_8, "__atomic_exchange_8", + // NULL, + // t, 0); + + // t = build_function_type_list (boolean_type_node, ptr_type_node, + // ptr_type_node, + // uint32_type_node, boolean_type_node, + // integer_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4, + // "__atomic_compare_exchange_4", NULL, t, 0); + + // t = build_function_type_list (boolean_type_node, ptr_type_node, + // ptr_type_node, + // uint64_type_node, boolean_type_node, + // integer_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8, + // "__atomic_compare_exchange_8", NULL, t, 0); + + // t = build_function_type_list (uint32_type_node, ptr_type_node, + // uint32_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_4, "__atomic_add_fetch_4", + // NULL, t, 0); + + // t = build_function_type_list (uint64_type_node, ptr_type_node, + // uint64_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_8, "__atomic_add_fetch_8", + // NULL, t, 0); + + // t = build_function_type_list (unsigned_char_type_node, ptr_type_node, + // unsigned_char_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_AND_FETCH_1, "__atomic_and_fetch_1", + // NULL, t, 0); + // this->define_builtin (BUILT_IN_ATOMIC_FETCH_AND_1, "__atomic_fetch_and_1", + // NULL, t, 0); + + // t = build_function_type_list (unsigned_char_type_node, ptr_type_node, + // unsigned_char_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1", + // NULL, + // t, 0); + // this->define_builtin (BUILT_IN_ATOMIC_FETCH_OR_1, "__atomic_fetch_or_1", + // NULL, + // t, 0); +} + +// Get an unnamed integer type. + +int +Gcc_backend::get_pointer_size () +{ + return POINTER_SIZE; +} + +tree +Gcc_backend::raw_str_type () +{ + tree char_ptr = build_pointer_type (char_type_node); + tree const_char_type = build_qualified_type (char_ptr, TYPE_QUAL_CONST); + return const_char_type; +} + +tree +Gcc_backend::integer_type (bool is_unsigned, int bits) +{ + tree type; + if (is_unsigned) + { + if (bits == INT_TYPE_SIZE) + type = unsigned_type_node; + else if (bits == SHORT_TYPE_SIZE) + type = short_unsigned_type_node; + else if (bits == LONG_TYPE_SIZE) + type = long_unsigned_type_node; + else if (bits == LONG_LONG_TYPE_SIZE) + type = long_long_unsigned_type_node; + else + type = make_unsigned_type (bits); + } + else + { + if (bits == INT_TYPE_SIZE) + type = integer_type_node; + else if (bits == SHORT_TYPE_SIZE) + type = short_integer_type_node; + else if (bits == LONG_TYPE_SIZE) + type = long_integer_type_node; + else if (bits == LONG_LONG_TYPE_SIZE) + type = long_long_integer_type_node; + else + type = make_signed_type (bits); + } + return type; +} + +// Get an unnamed float type. + +tree +Gcc_backend::float_type (int bits) +{ + tree type; + if (bits == FLOAT_TYPE_SIZE) + type = float_type_node; + else if (bits == DOUBLE_TYPE_SIZE) + type = double_type_node; + else if (bits == LONG_DOUBLE_TYPE_SIZE) + type = long_double_type_node; + else + { + type = make_node (REAL_TYPE); + TYPE_PRECISION (type) = bits; + layout_type (type); + } + return type; +} + +// Get an unnamed complex type. + +tree +Gcc_backend::complex_type (int bits) +{ + tree type; + if (bits == FLOAT_TYPE_SIZE * 2) + type = complex_float_type_node; + else if (bits == DOUBLE_TYPE_SIZE * 2) + type = complex_double_type_node; + else if (bits == LONG_DOUBLE_TYPE_SIZE * 2) + type = complex_long_double_type_node; + else + { + type = make_node (REAL_TYPE); + TYPE_PRECISION (type) = bits / 2; + layout_type (type); + type = build_complex_type (type); + } + return type; +} + +// Get a pointer type. + +tree +Gcc_backend::pointer_type (tree to_type) +{ + if (to_type == error_mark_node) + return error_mark_node; + tree type = build_pointer_type (to_type); + return type; +} + +// Get a reference type. + +tree +Gcc_backend::reference_type (tree to_type) +{ + if (to_type == error_mark_node) + return error_mark_node; + tree type = build_reference_type (to_type); + return type; +} + +// Get immutable type + +tree +Gcc_backend::immutable_type (tree base) +{ + if (base == error_mark_node) + return error_mark_node; + tree constified = build_qualified_type (base, TYPE_QUAL_CONST); + return constified; +} + +// Make a function type. + +tree +Gcc_backend::function_type (const typed_identifier &receiver, + const std::vector ¶meters, + const std::vector &results, + tree result_struct, Location) +{ + tree args = NULL_TREE; + tree *pp = &args; + if (receiver.type != NULL_TREE) + { + tree t = receiver.type; + if (t == error_mark_node) + return error_mark_node; + *pp = tree_cons (NULL_TREE, t, NULL_TREE); + pp = &TREE_CHAIN (*pp); + } + + for (std::vector::const_iterator p = parameters.begin (); + p != parameters.end (); ++p) + { + tree t = p->type; + if (t == error_mark_node) + return error_mark_node; + *pp = tree_cons (NULL_TREE, t, NULL_TREE); + pp = &TREE_CHAIN (*pp); + } + + // Varargs is handled entirely at the Rust level. When converted to + // GENERIC functions are not varargs. + *pp = void_list_node; + + tree result; + if (results.empty ()) + result = void_type_node; + else if (results.size () == 1) + result = results.front ().type; + else + { + gcc_assert (result_struct != NULL); + result = result_struct; + } + if (result == error_mark_node) + return error_mark_node; + + // The libffi library cannot represent a zero-sized object. To + // avoid causing confusion on 32-bit SPARC, we treat a function that + // returns a zero-sized value as returning void. That should do no + // harm since there is no actual value to be returned. See + // https://gcc.gnu.org/PR72814 for details. + if (result != void_type_node && int_size_in_bytes (result) == 0) + result = void_type_node; + + tree fntype = build_function_type (result, args); + if (fntype == error_mark_node) + return error_mark_node; + + return build_pointer_type (fntype); +} + +tree +Gcc_backend::function_type_varadic ( + const typed_identifier &receiver, + const std::vector ¶meters, + const std::vector &results, tree result_struct, Location) +{ + size_t n = parameters.size () + (receiver.type != NULL_TREE ? 1 : 0); + tree *args = XALLOCAVEC (tree, n); + size_t offs = 0; + + if (receiver.type != NULL_TREE) + { + tree t = receiver.type; + if (t == error_mark_node) + return error_mark_node; + + args[offs++] = t; + } + + for (std::vector::const_iterator p = parameters.begin (); + p != parameters.end (); ++p) + { + tree t = p->type; + if (t == error_mark_node) + return error_mark_node; + args[offs++] = t; + } + + tree result; + if (results.empty ()) + result = void_type_node; + else if (results.size () == 1) + result = results.front ().type; + else + { + gcc_assert (result_struct != NULL_TREE); + result = result_struct; + } + if (result == error_mark_node) + return error_mark_node; + + // The libffi library cannot represent a zero-sized object. To + // avoid causing confusion on 32-bit SPARC, we treat a function that + // returns a zero-sized value as returning void. That should do no + // harm since there is no actual value to be returned. See + // https://gcc.gnu.org/PR72814 for details. + if (result != void_type_node && int_size_in_bytes (result) == 0) + result = void_type_node; + + tree fntype = build_varargs_function_type_array (result, n, args); + if (fntype == error_mark_node) + return error_mark_node; + + return build_pointer_type (fntype); +} + +tree +Gcc_backend::function_ptr_type (tree result_type, + const std::vector ¶meters, + Location /* locus */) +{ + tree args = NULL_TREE; + tree *pp = &args; + + for (auto ¶m : parameters) + { + if (param == error_mark_node) + return error_mark_node; + + *pp = tree_cons (NULL_TREE, param, NULL_TREE); + pp = &TREE_CHAIN (*pp); + } + + *pp = void_list_node; + + tree result = result_type; + if (result != void_type_node && int_size_in_bytes (result) == 0) + result = void_type_node; + + tree fntype = build_function_type (result, args); + if (fntype == error_mark_node) + return error_mark_node; + + return build_pointer_type (fntype); +} + +// Make a struct type. + +tree +Gcc_backend::struct_type (const std::vector &fields) +{ + return this->fill_in_fields (make_node (RECORD_TYPE), fields); +} + +// Make a union type. + +tree +Gcc_backend::union_type (const std::vector &fields) +{ + return this->fill_in_fields (make_node (UNION_TYPE), fields); +} + +// Fill in the fields of a struct or union type. + +tree +Gcc_backend::fill_in_fields (tree fill, + const std::vector &fields) +{ + tree field_trees = NULL_TREE; + tree *pp = &field_trees; + for (std::vector::const_iterator p = fields.begin (); + p != fields.end (); ++p) + { + tree name_tree = get_identifier_from_string (p->name); + tree type_tree = p->type; + if (type_tree == error_mark_node) + return error_mark_node; + tree field = build_decl (p->location.gcc_location (), FIELD_DECL, + name_tree, type_tree); + DECL_CONTEXT (field) = fill; + *pp = field; + pp = &DECL_CHAIN (field); + } + TYPE_FIELDS (fill) = field_trees; + layout_type (fill); + + // Because Rust permits converting between named struct types and + // equivalent struct types, for which we use VIEW_CONVERT_EXPR, and + // because we don't try to maintain TYPE_CANONICAL for struct types, + // we need to tell the middle-end to use structural equality. + SET_TYPE_STRUCTURAL_EQUALITY (fill); + + return fill; +} + +// Make an array type. + +tree +Gcc_backend::array_type (tree element_type, tree length) +{ + return this->fill_in_array (make_node (ARRAY_TYPE), element_type, length); +} + +// Fill in an array type. + +tree +Gcc_backend::fill_in_array (tree fill, tree element_type, tree length_tree) +{ + if (element_type == error_mark_node || length_tree == error_mark_node) + return error_mark_node; + + gcc_assert (TYPE_SIZE (element_type) != NULL_TREE); + + length_tree = fold_convert (sizetype, length_tree); + + // build_index_type takes the maximum index, which is one less than + // the length. + tree index_type_tree = build_index_type ( + fold_build2 (MINUS_EXPR, sizetype, length_tree, size_one_node)); + + TREE_TYPE (fill) = element_type; + TYPE_DOMAIN (fill) = index_type_tree; + TYPE_ADDR_SPACE (fill) = TYPE_ADDR_SPACE (element_type); + layout_type (fill); + + if (TYPE_STRUCTURAL_EQUALITY_P (element_type)) + SET_TYPE_STRUCTURAL_EQUALITY (fill); + else if (TYPE_CANONICAL (element_type) != element_type + || TYPE_CANONICAL (index_type_tree) != index_type_tree) + TYPE_CANONICAL (fill) = build_array_type (TYPE_CANONICAL (element_type), + TYPE_CANONICAL (index_type_tree)); + + return fill; +} + +// Return a named version of a type. + +tree +Gcc_backend::named_type (const std::string &name, tree type, Location location) +{ + if (type == error_mark_node) + return error_mark_node; + + // The middle-end expects a basic type to have a name. In Rust every + // basic type will have a name. The first time we see a basic type, + // give it whatever Rust name we have at this point. + if (TYPE_NAME (type) == NULL_TREE + && location.gcc_location () == BUILTINS_LOCATION + && (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == REAL_TYPE + || TREE_CODE (type) == COMPLEX_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE)) + { + tree decl = build_decl (BUILTINS_LOCATION, TYPE_DECL, + get_identifier_from_string (name), type); + TYPE_NAME (type) = decl; + return type; + } + + tree copy = build_variant_type_copy (type); + tree decl = build_decl (location.gcc_location (), TYPE_DECL, + get_identifier_from_string (name), copy); + DECL_ORIGINAL_TYPE (decl) = type; + TYPE_NAME (copy) = decl; + return copy; +} + +// Return the size of a type. + +int64_t +Gcc_backend::type_size (tree t) +{ + if (t == error_mark_node) + return 1; + if (t == void_type_node) + return 0; + t = TYPE_SIZE_UNIT (t); + gcc_assert (tree_fits_uhwi_p (t)); + unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW (t); + int64_t ret = static_cast (val_wide); + if (ret < 0 || static_cast (ret) != val_wide) + return -1; + return ret; +} + +// Return the alignment of a type. + +int64_t +Gcc_backend::type_alignment (tree t) +{ + if (t == error_mark_node) + return 1; + return TYPE_ALIGN_UNIT (t); +} + +// Return the alignment of a struct field of type BTYPE. + +int64_t +Gcc_backend::type_field_alignment (tree t) +{ + if (t == error_mark_node) + return 1; + return rust_field_alignment (t); +} + +// Return the offset of a field in a struct. + +int64_t +Gcc_backend::type_field_offset (tree struct_tree, size_t index) +{ + if (struct_tree == error_mark_node) + return 0; + gcc_assert (TREE_CODE (struct_tree) == RECORD_TYPE); + tree field = TYPE_FIELDS (struct_tree); + for (; index > 0; --index) + { + field = DECL_CHAIN (field); + gcc_assert (field != NULL_TREE); + } + HOST_WIDE_INT offset_wide = int_byte_position (field); + int64_t ret = static_cast (offset_wide); + gcc_assert (ret == offset_wide); + return ret; +} + +// Return the zero value for a type. + +tree +Gcc_backend::zero_expression (tree t) +{ + tree ret; + if (t == error_mark_node) + ret = error_mark_node; + else + ret = build_zero_cst (t); + return ret; +} + +// An expression that references a variable. + +tree +Gcc_backend::var_expression (Bvariable *var, Location location) +{ + return var->get_tree (location); +} + +// Return a typed value as a constant integer. +// This function does not release the memory of @val + +tree +Gcc_backend::integer_constant_expression (tree t, mpz_t val) +{ + if (t == error_mark_node) + return error_mark_node; + + tree ret = wide_int_to_tree (t, wi::from_mpz (t, val, true)); + return ret; +} + +// Return a typed value as a constant floating-point number. + +tree +Gcc_backend::float_constant_expression (tree t, mpfr_t val) +{ + tree ret; + if (t == error_mark_node) + return error_mark_node; + + REAL_VALUE_TYPE r1; + real_from_mpfr (&r1, val, t, GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert (&r2, TYPE_MODE (t), &r1); + ret = build_real (t, r2); + return ret; +} + +// Return a typed real and imaginary value as a constant complex number. + +tree +Gcc_backend::complex_constant_expression (tree t, mpc_t val) +{ + tree ret; + if (t == error_mark_node) + return error_mark_node; + + REAL_VALUE_TYPE r1; + real_from_mpfr (&r1, mpc_realref (val), TREE_TYPE (t), GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert (&r2, TYPE_MODE (TREE_TYPE (t)), &r1); + + REAL_VALUE_TYPE r3; + real_from_mpfr (&r3, mpc_imagref (val), TREE_TYPE (t), GMP_RNDN); + REAL_VALUE_TYPE r4; + real_convert (&r4, TYPE_MODE (TREE_TYPE (t)), &r3); + + ret = build_complex (t, build_real (TREE_TYPE (t), r2), + build_real (TREE_TYPE (t), r4)); + return ret; +} + +// Make a constant string expression. + +tree +Gcc_backend::string_constant_expression (const std::string &val) +{ + tree index_type = build_index_type (size_int (val.length ())); + tree const_char_type = build_qualified_type (char_type_node, TYPE_QUAL_CONST); + tree string_type = build_array_type (const_char_type, index_type); + TYPE_STRING_FLAG (string_type) = 1; + tree string_val = build_string (val.length (), val.data ()); + TREE_TYPE (string_val) = string_type; + + return string_val; +} + +tree +Gcc_backend::wchar_constant_expression (wchar_t c) +{ + return build_int_cst (this->wchar_type (), c); +} + +tree +Gcc_backend::char_constant_expression (char c) +{ + return build_int_cst (this->char_type (), c); +} + +// Make a constant boolean expression. + +tree +Gcc_backend::boolean_constant_expression (bool val) +{ + return val ? boolean_true_node : boolean_false_node; +} + +// Return the real part of a complex expression. + +tree +Gcc_backend::real_part_expression (tree complex_tree, Location location) +{ + if (complex_tree == error_mark_node) + return error_mark_node; + gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree))); + tree ret + = fold_build1_loc (location.gcc_location (), REALPART_EXPR, + TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree); + return ret; +} + +// Return the imaginary part of a complex expression. + +tree +Gcc_backend::imag_part_expression (tree complex_tree, Location location) +{ + if (complex_tree == error_mark_node) + return error_mark_node; + gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree))); + tree ret + = fold_build1_loc (location.gcc_location (), IMAGPART_EXPR, + TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree); + return ret; +} + +// Make a complex expression given its real and imaginary parts. + +tree +Gcc_backend::complex_expression (tree real_tree, tree imag_tree, + Location location) +{ + if (real_tree == error_mark_node || imag_tree == error_mark_node) + return error_mark_node; + gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real_tree)) + == TYPE_MAIN_VARIANT (TREE_TYPE (imag_tree))); + gcc_assert (SCALAR_FLOAT_TYPE_P (TREE_TYPE (real_tree))); + tree ret = fold_build2_loc (location.gcc_location (), COMPLEX_EXPR, + build_complex_type (TREE_TYPE (real_tree)), + real_tree, imag_tree); + return ret; +} + +// An expression that converts an expression to a different type. + +tree +Gcc_backend::convert_expression (tree type_tree, tree expr_tree, + Location location) +{ + if (type_tree == error_mark_node || expr_tree == error_mark_node + || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + tree ret; + if (this->type_size (type_tree) == 0 + || TREE_TYPE (expr_tree) == void_type_node) + { + // Do not convert zero-sized types. + ret = expr_tree; + } + else if (TREE_CODE (type_tree) == INTEGER_TYPE) + ret = convert_to_integer (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == REAL_TYPE) + ret = convert_to_real (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == COMPLEX_TYPE) + ret = convert_to_complex (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE) + ret = convert_to_pointer (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == RECORD_TYPE + || TREE_CODE (type_tree) == ARRAY_TYPE) + ret = fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR, + type_tree, expr_tree); + else + ret = fold_convert_loc (location.gcc_location (), type_tree, expr_tree); + + return ret; +} + +// Return an expression for the field at INDEX in BSTRUCT. + +tree +Gcc_backend::struct_field_expression (tree struct_tree, size_t index, + Location location) +{ + if (struct_tree == error_mark_node + || TREE_TYPE (struct_tree) == error_mark_node) + return error_mark_node; + gcc_assert (TREE_CODE (TREE_TYPE (struct_tree)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (struct_tree)) == UNION_TYPE); + tree field = TYPE_FIELDS (TREE_TYPE (struct_tree)); + if (field == NULL_TREE) + { + // This can happen for a type which refers to itself indirectly + // and then turns out to be erroneous. + return error_mark_node; + } + for (unsigned int i = index; i > 0; --i) + { + field = DECL_CHAIN (field); + gcc_assert (field != NULL_TREE); + } + if (TREE_TYPE (field) == error_mark_node) + return error_mark_node; + tree ret = fold_build3_loc (location.gcc_location (), COMPONENT_REF, + TREE_TYPE (field), struct_tree, field, NULL_TREE); + if (TREE_CONSTANT (struct_tree)) + TREE_CONSTANT (ret) = 1; + return ret; +} + +// Return an expression that executes BSTAT before BEXPR. + +tree +Gcc_backend::compound_expression (tree stat, tree expr, Location location) +{ + if (stat == error_mark_node || expr == error_mark_node) + return error_mark_node; + tree ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, + TREE_TYPE (expr), stat, expr); + return ret; +} + +// Return an expression that executes THEN_EXPR if CONDITION is true, or +// ELSE_EXPR otherwise. + +tree +Gcc_backend::conditional_expression (tree, tree type_tree, tree cond_expr, + tree then_expr, tree else_expr, + Location location) +{ + if (type_tree == error_mark_node || cond_expr == error_mark_node + || then_expr == error_mark_node || else_expr == error_mark_node) + return error_mark_node; + tree ret = build3_loc (location.gcc_location (), COND_EXPR, type_tree, + cond_expr, then_expr, else_expr); + return ret; +} + +/* Helper function that converts rust operators to equivalent GCC tree_code. + Note that CompoundAssignmentOperator don't get their corresponding tree_code, + because they get compiled away when we lower AST to HIR. */ +static enum tree_code +operator_to_tree_code (NegationOperator op) +{ + switch (op) + { + case NegationOperator::NEGATE: + return NEGATE_EXPR; + case NegationOperator::NOT: + return TRUTH_NOT_EXPR; + default: + gcc_unreachable (); + } +} + +/* Note that GCC tree code distinguishes floating point division and integer + division. These two types of division are represented as the same rust + operator, and can only be distinguished via context(i.e. the TREE_TYPE of the + operands). */ +static enum tree_code +operator_to_tree_code (ArithmeticOrLogicalOperator op, bool floating_point) +{ + switch (op) + { + case ArithmeticOrLogicalOperator::ADD: + return PLUS_EXPR; + case ArithmeticOrLogicalOperator::SUBTRACT: + return MINUS_EXPR; + case ArithmeticOrLogicalOperator::MULTIPLY: + return MULT_EXPR; + case ArithmeticOrLogicalOperator::DIVIDE: + if (floating_point) + return RDIV_EXPR; + else + return TRUNC_DIV_EXPR; + case ArithmeticOrLogicalOperator::MODULUS: + return TRUNC_MOD_EXPR; + case ArithmeticOrLogicalOperator::BITWISE_AND: + return BIT_AND_EXPR; + case ArithmeticOrLogicalOperator::BITWISE_OR: + return BIT_IOR_EXPR; + case ArithmeticOrLogicalOperator::BITWISE_XOR: + return BIT_XOR_EXPR; + case ArithmeticOrLogicalOperator::LEFT_SHIFT: + return LSHIFT_EXPR; + case ArithmeticOrLogicalOperator::RIGHT_SHIFT: + return RSHIFT_EXPR; + default: + gcc_unreachable (); + } +} + +static enum tree_code +operator_to_tree_code (ComparisonOperator op) +{ + switch (op) + { + case ComparisonOperator::EQUAL: + return EQ_EXPR; + case ComparisonOperator::NOT_EQUAL: + return NE_EXPR; + case ComparisonOperator::GREATER_THAN: + return GT_EXPR; + case ComparisonOperator::LESS_THAN: + return LT_EXPR; + case ComparisonOperator::GREATER_OR_EQUAL: + return GE_EXPR; + case ComparisonOperator::LESS_OR_EQUAL: + return LE_EXPR; + default: + gcc_unreachable (); + } +} + +static enum tree_code +operator_to_tree_code (LazyBooleanOperator op) +{ + switch (op) + { + case LazyBooleanOperator::LOGICAL_OR: + return TRUTH_ORIF_EXPR; + case LazyBooleanOperator::LOGICAL_AND: + return TRUTH_ANDIF_EXPR; + default: + gcc_unreachable (); + } +} + +/* Helper function for deciding if a tree is a floating point node. */ +bool +is_floating_point (tree t) +{ + auto tree_type = TREE_CODE (TREE_TYPE (t)); + return tree_type == REAL_TYPE || tree_type == COMPLEX_TYPE; +} + +// Return an expression for the negation operation OP EXPR. +tree +Gcc_backend::negation_expression (NegationOperator op, tree expr_tree, + Location location) +{ + /* Check if the expression is an error, in which case we return an error + expression. */ + if (expr_tree == error_mark_node || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + /* For negation operators, the resulting type should be the same as its + operand. */ + auto tree_type = TREE_TYPE (expr_tree); + auto original_type = tree_type; + auto tree_code = operator_to_tree_code (op); + + /* For floating point operations we may need to extend the precision of type. + For example, a 64-bit machine may not support operations on float32. */ + bool floating_point = is_floating_point (expr_tree); + auto extended_type = NULL_TREE; + if (floating_point) + { + extended_type = excess_precision_type (tree_type); + if (extended_type != NULL_TREE) + { + expr_tree = convert (extended_type, expr_tree); + tree_type = extended_type; + } + } + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build1_loc (location.gcc_location (), tree_code, + tree_type, expr_tree); + if (floating_point && extended_type != NULL_TREE) + new_tree = convert (original_type, expr_tree); + return new_tree; +} + +// Return an expression for the arithmetic or logical operation LEFT OP RIGHT. +tree +Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op, + tree left_tree, tree right_tree, + Location location) +{ + /* Check if either expression is an error, in which case we return an error + expression. */ + if (left_tree == error_mark_node || right_tree == error_mark_node) + return error_mark_node; + + /* We need to determine if we're doing floating point arithmetics of integer + arithmetics. */ + bool floating_point = is_floating_point (left_tree); + + /* For arithmetic or logical operators, the resulting type should be the same + as the lhs operand. */ + auto tree_type = TREE_TYPE (left_tree); + auto original_type = tree_type; + auto tree_code = operator_to_tree_code (op, floating_point); + + /* For floating point operations we may need to extend the precision of type. + For example, a 64-bit machine may not support operations on float32. */ + auto extended_type = NULL_TREE; + if (floating_point) + { + extended_type = excess_precision_type (tree_type); + if (extended_type != NULL_TREE) + { + left_tree = convert (extended_type, left_tree); + right_tree = convert (extended_type, right_tree); + tree_type = extended_type; + } + } + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build2_loc (location.gcc_location (), tree_code, + tree_type, left_tree, right_tree); + TREE_CONSTANT (new_tree) + = TREE_CONSTANT (left_tree) && TREE_CONSTANT (right_tree); + + if (floating_point && extended_type != NULL_TREE) + new_tree = convert (original_type, new_tree); + return new_tree; +} + +// Return an expression for the comparison operation LEFT OP RIGHT. +tree +Gcc_backend::comparison_expression (ComparisonOperator op, tree left_tree, + tree right_tree, Location location) +{ + /* Check if either expression is an error, in which case we return an error + expression. */ + if (left_tree == error_mark_node || right_tree == error_mark_node) + return error_mark_node; + + /* For comparison operators, the resulting type should be boolean. */ + auto tree_type = boolean_type_node; + auto tree_code = operator_to_tree_code (op); + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build2_loc (location.gcc_location (), tree_code, + tree_type, left_tree, right_tree); + return new_tree; +} + +// Return an expression for the lazy boolean operation LEFT OP RIGHT. +tree +Gcc_backend::lazy_boolean_expression (LazyBooleanOperator op, tree left_tree, + tree right_tree, Location location) +{ + /* Check if either expression is an error, in which case we return an error + expression. */ + if (left_tree == error_mark_node || right_tree == error_mark_node) + return error_mark_node; + + /* For lazy boolean operators, the resulting type should be the same as the + rhs operand. */ + auto tree_type = TREE_TYPE (right_tree); + auto tree_code = operator_to_tree_code (op); + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build2_loc (location.gcc_location (), tree_code, + tree_type, left_tree, right_tree); + return new_tree; +} + +// Return an expression that constructs BTYPE with VALS. + +tree +Gcc_backend::constructor_expression (tree type_tree, bool is_variant, + const std::vector &vals, + int union_index, Location location) +{ + if (type_tree == error_mark_node) + return error_mark_node; + + vec *init; + vec_alloc (init, vals.size ()); + + tree sink = NULL_TREE; + bool is_constant = true; + tree field = TYPE_FIELDS (type_tree); + + if (is_variant) + { + gcc_assert (union_index != -1); + gcc_assert (TREE_CODE (type_tree) == UNION_TYPE); + + for (int i = 0; i < union_index; i++) + { + gcc_assert (field != NULL_TREE); + field = DECL_CHAIN (field); + } + + tree nested_ctor + = constructor_expression (TREE_TYPE (field), false, vals, -1, location); + + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = field; + elt->value + = this->convert_tree (TREE_TYPE (field), nested_ctor, location); + if (!TREE_CONSTANT (elt->value)) + is_constant = false; + } + else + { + if (union_index != -1) + { + gcc_assert (TREE_CODE (type_tree) == UNION_TYPE); + tree val = vals.front (); + for (int i = 0; i < union_index; i++) + { + gcc_assert (field != NULL_TREE); + field = DECL_CHAIN (field); + } + if (TREE_TYPE (field) == error_mark_node || val == error_mark_node + || TREE_TYPE (val) == error_mark_node) + return error_mark_node; + + if (int_size_in_bytes (TREE_TYPE (field)) == 0) + { + // GIMPLE cannot represent indices of zero-sized types so + // trying to construct a map with zero-sized keys might lead + // to errors. Instead, we evaluate each expression that + // would have been added as a map element for its + // side-effects and construct an empty map. + append_to_statement_list (val, &sink); + } + else + { + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = field; + elt->value + = this->convert_tree (TREE_TYPE (field), val, location); + if (!TREE_CONSTANT (elt->value)) + is_constant = false; + } + } + else + { + gcc_assert (TREE_CODE (type_tree) == RECORD_TYPE); + for (std::vector::const_iterator p = vals.begin (); + p != vals.end (); ++p, field = DECL_CHAIN (field)) + { + gcc_assert (field != NULL_TREE); + tree val = (*p); + if (TREE_TYPE (field) == error_mark_node || val == error_mark_node + || TREE_TYPE (val) == error_mark_node) + return error_mark_node; + + if (int_size_in_bytes (TREE_TYPE (field)) == 0) + { + // GIMPLE cannot represent indices of zero-sized types so + // trying to construct a map with zero-sized keys might lead + // to errors. Instead, we evaluate each expression that + // would have been added as a map element for its + // side-effects and construct an empty map. + append_to_statement_list (val, &sink); + continue; + } + + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = field; + elt->value + = this->convert_tree (TREE_TYPE (field), val, location); + if (!TREE_CONSTANT (elt->value)) + is_constant = false; + } + gcc_assert (field == NULL_TREE); + } + } + + tree ret = build_constructor (type_tree, init); + if (is_constant) + TREE_CONSTANT (ret) = 1; + if (sink != NULL_TREE) + ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree, + sink, ret); + return ret; +} + +tree +Gcc_backend::array_constructor_expression ( + tree type_tree, const std::vector &indexes, + const std::vector &vals, Location location) +{ + if (type_tree == error_mark_node) + return error_mark_node; + + gcc_assert (indexes.size () == vals.size ()); + + tree element_type = TREE_TYPE (type_tree); + HOST_WIDE_INT element_size = int_size_in_bytes (element_type); + vec *init; + vec_alloc (init, element_size == 0 ? 0 : vals.size ()); + + tree sink = NULL_TREE; + bool is_constant = true; + for (size_t i = 0; i < vals.size (); ++i) + { + tree index = size_int (indexes[i]); + tree val = vals[i]; + + if (index == error_mark_node || val == error_mark_node) + return error_mark_node; + + if (element_size == 0) + { + // GIMPLE cannot represent arrays of zero-sized types so trying + // to construct an array of zero-sized values might lead to errors. + // Instead, we evaluate each expression that would have been added as + // an array value for its side-effects and construct an empty array. + append_to_statement_list (val, &sink); + continue; + } + + if (!TREE_CONSTANT (val)) + is_constant = false; + + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = index; + elt->value = val; + } + + tree ret = build_constructor (type_tree, init); + if (is_constant) + TREE_CONSTANT (ret) = 1; + if (sink != NULL_TREE) + ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree, + sink, ret); + return ret; +} + +// Build insns to create an array, initialize all elements of the array to +// value, and return it +tree +Gcc_backend::array_initializer (tree fndecl, tree block, tree array_type, + tree length, tree value, tree *tmp, + Location locus) +{ + std::vector stmts; + + // Temporary array we initialize with the desired value. + tree t = NULL_TREE; + Bvariable *tmp_array = this->temporary_variable (fndecl, block, array_type, + NULL_TREE, true, locus, &t); + tree arr = tmp_array->get_tree (locus); + stmts.push_back (t); + + // Temporary for the array length used for initialization loop guard. + Bvariable *tmp_len = this->temporary_variable (fndecl, block, size_type_node, + length, true, locus, &t); + tree len = tmp_len->get_tree (locus); + stmts.push_back (t); + + // Temporary variable for pointer used to initialize elements. + tree ptr_type = this->pointer_type (TREE_TYPE (array_type)); + tree ptr_init + = build1_loc (locus.gcc_location (), ADDR_EXPR, ptr_type, + this->array_index_expression (arr, integer_zero_node, locus)); + Bvariable *tmp_ptr = this->temporary_variable (fndecl, block, ptr_type, + ptr_init, false, locus, &t); + tree ptr = tmp_ptr->get_tree (locus); + stmts.push_back (t); + + // push statement list for the loop + std::vector loop_stmts; + + // Loop exit condition: + // if (length == 0) break; + t = this->comparison_expression (ComparisonOperator::EQUAL, len, + this->zero_expression (TREE_TYPE (len)), + locus); + + t = this->exit_expression (t, locus); + loop_stmts.push_back (t); + + // Assign value to the current pointer position + // *ptr = value; + t = this->assignment_statement (build_fold_indirect_ref (ptr), value, locus); + loop_stmts.push_back (t); + + // Move pointer to next element + // ptr++; + tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptr_type)); + t = build2 (POSTINCREMENT_EXPR, ptr_type, ptr, convert (ptr_type, size)); + loop_stmts.push_back (t); + + // Decrement loop counter. + // length--; + t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (len), len, + convert (TREE_TYPE (len), integer_one_node)); + loop_stmts.push_back (t); + + // pop statments and finish loop + tree loop_body = this->statement_list (loop_stmts); + stmts.push_back (this->loop_expression (loop_body, locus)); + + // Return the temporary in the provided pointer and the statement list which + // initializes it. + *tmp = tmp_array->get_tree (locus); + return this->statement_list (stmts); +} + +// Return an expression representing ARRAY[INDEX] + +tree +Gcc_backend::array_index_expression (tree array_tree, tree index_tree, + Location location) +{ + if (array_tree == error_mark_node || TREE_TYPE (array_tree) == error_mark_node + || index_tree == error_mark_node) + return error_mark_node; + + // A function call that returns a zero sized object will have been + // changed to return void. If we see void here, assume we are + // dealing with a zero sized type and just evaluate the operands. + tree ret; + if (TREE_TYPE (array_tree) != void_type_node) + ret = build4_loc (location.gcc_location (), ARRAY_REF, + TREE_TYPE (TREE_TYPE (array_tree)), array_tree, + index_tree, NULL_TREE, NULL_TREE); + else + ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, + void_type_node, array_tree, index_tree); + + return ret; +} + +// Create an expression for a call to FN_EXPR with FN_ARGS. +tree +Gcc_backend::call_expression (tree fn, const std::vector &fn_args, + tree chain_expr, Location location) +{ + if (fn == error_mark_node || TREE_TYPE (fn) == error_mark_node) + return error_mark_node; + + gcc_assert (FUNCTION_POINTER_TYPE_P (TREE_TYPE (fn))); + tree rettype = TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))); + + size_t nargs = fn_args.size (); + tree *args = nargs == 0 ? NULL : new tree[nargs]; + for (size_t i = 0; i < nargs; ++i) + { + args[i] = fn_args.at (i); + } + + tree fndecl = fn; + if (TREE_CODE (fndecl) == ADDR_EXPR) + fndecl = TREE_OPERAND (fndecl, 0); + + // This is to support builtin math functions when using 80387 math. + tree excess_type = NULL_TREE; + if (optimize && TREE_CODE (fndecl) == FUNCTION_DECL + && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL) + && DECL_IS_UNDECLARED_BUILTIN (fndecl) && nargs > 0 + && ((SCALAR_FLOAT_TYPE_P (rettype) + && SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[0]))) + || (COMPLEX_FLOAT_TYPE_P (rettype) + && COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[0]))))) + { + excess_type = excess_precision_type (TREE_TYPE (args[0])); + if (excess_type != NULL_TREE) + { + tree excess_fndecl + = mathfn_built_in (excess_type, DECL_FUNCTION_CODE (fndecl)); + if (excess_fndecl == NULL_TREE) + excess_type = NULL_TREE; + else + { + fn = build_fold_addr_expr_loc (location.gcc_location (), + excess_fndecl); + for (size_t i = 0; i < nargs; ++i) + { + if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[i])) + || COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[i]))) + args[i] = ::convert (excess_type, args[i]); + } + } + } + } + + tree ret + = build_call_array_loc (location.gcc_location (), + excess_type != NULL_TREE ? excess_type : rettype, + fn, nargs, args); + + // check for deprecated function usage + if (fndecl && TREE_DEPRECATED (fndecl)) + { + // set up the call-site information for `warn_deprecated_use` + input_location = location.gcc_location (); + warn_deprecated_use (fndecl, NULL_TREE); + } + + if (chain_expr) + CALL_EXPR_STATIC_CHAIN (ret) = chain_expr; + + if (excess_type != NULL_TREE) + { + // Calling convert here can undo our excess precision change. + // That may or may not be a bug in convert_to_real. + ret = build1_loc (location.gcc_location (), NOP_EXPR, rettype, ret); + } + + delete[] args; + return ret; +} + +// Variable initialization. + +tree +Gcc_backend::init_statement (tree, Bvariable *var, tree init_tree) +{ + tree var_tree = var->get_decl (); + if (var_tree == error_mark_node || init_tree == error_mark_node) + return error_mark_node; + gcc_assert (TREE_CODE (var_tree) == VAR_DECL); + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // initialization of a zero-sized expression to a non-zero sized + // variable, or vice-versa. Avoid crashes by omitting the + // initializer. Such initializations don't mean anything anyhow. + if (int_size_in_bytes (TREE_TYPE (var_tree)) != 0 && init_tree != NULL_TREE + && TREE_TYPE (init_tree) != void_type_node + && int_size_in_bytes (TREE_TYPE (init_tree)) != 0) + { + DECL_INITIAL (var_tree) = init_tree; + init_tree = NULL_TREE; + } + + tree ret = build1_loc (DECL_SOURCE_LOCATION (var_tree), DECL_EXPR, + void_type_node, var_tree); + if (init_tree != NULL_TREE) + ret = build2_loc (DECL_SOURCE_LOCATION (var_tree), COMPOUND_EXPR, + void_type_node, init_tree, ret); + + return ret; +} + +// Assignment. + +tree +Gcc_backend::assignment_statement (tree lhs, tree rhs, Location location) +{ + if (lhs == error_mark_node || rhs == error_mark_node) + return error_mark_node; + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // assignment of a zero-sized expression to a non-zero sized + // expression; avoid crashes here by avoiding assignments of + // zero-sized expressions. Such assignments don't really mean + // anything anyhow. + if (TREE_TYPE (lhs) == void_type_node + || int_size_in_bytes (TREE_TYPE (lhs)) == 0 + || TREE_TYPE (rhs) == void_type_node + || int_size_in_bytes (TREE_TYPE (rhs)) == 0) + return this->compound_statement (lhs, rhs); + + rhs = this->convert_tree (TREE_TYPE (lhs), rhs, location); + + return fold_build2_loc (location.gcc_location (), MODIFY_EXPR, void_type_node, + lhs, rhs); +} + +// Return. + +tree +Gcc_backend::return_statement (tree fntree, const std::vector &vals, + Location location) +{ + if (fntree == error_mark_node) + return error_mark_node; + tree result = DECL_RESULT (fntree); + if (result == error_mark_node) + return error_mark_node; + + // If the result size is zero bytes, we have set the function type + // to have a result type of void, so don't return anything. + // See the function_type method. + tree res_type = TREE_TYPE (result); + if (res_type == void_type_node || int_size_in_bytes (res_type) == 0) + { + tree stmt_list = NULL_TREE; + for (std::vector::const_iterator p = vals.begin (); + p != vals.end (); p++) + { + tree val = (*p); + if (val == error_mark_node) + return error_mark_node; + append_to_statement_list (val, &stmt_list); + } + tree ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, NULL_TREE); + append_to_statement_list (ret, &stmt_list); + return stmt_list; + } + + tree ret; + if (vals.empty ()) + ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, NULL_TREE); + else if (vals.size () == 1) + { + tree val = vals.front (); + if (val == error_mark_node) + return error_mark_node; + tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR, + void_type_node, result, vals.front ()); + ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, set); + } + else + { + // To return multiple values, copy the values into a temporary + // variable of the right structure type, and then assign the + // temporary variable to the DECL_RESULT in the return + // statement. + tree stmt_list = NULL_TREE; + tree rettype = TREE_TYPE (result); + + if (DECL_STRUCT_FUNCTION (fntree) == NULL) + push_struct_function (fntree); + else + push_cfun (DECL_STRUCT_FUNCTION (fntree)); + tree rettmp = create_tmp_var (rettype, "RESULT"); + pop_cfun (); + + tree field = TYPE_FIELDS (rettype); + for (std::vector::const_iterator p = vals.begin (); + p != vals.end (); p++, field = DECL_CHAIN (field)) + { + gcc_assert (field != NULL_TREE); + tree ref + = fold_build3_loc (location.gcc_location (), COMPONENT_REF, + TREE_TYPE (field), rettmp, field, NULL_TREE); + tree val = (*p); + if (val == error_mark_node) + return error_mark_node; + tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR, + void_type_node, ref, (*p)); + append_to_statement_list (set, &stmt_list); + } + gcc_assert (field == NULL_TREE); + tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR, + void_type_node, result, rettmp); + tree ret_expr = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, set); + append_to_statement_list (ret_expr, &stmt_list); + ret = stmt_list; + } + return ret; +} + +// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if an +// error 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 Rust +// functions. In C++, the resulting code is of this form: +// try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; } + +tree +Gcc_backend::exception_handler_statement (tree try_stmt, tree except_stmt, + tree finally_stmt, Location location) +{ + if (try_stmt == error_mark_node || except_stmt == error_mark_node + || finally_stmt == error_mark_node) + return error_mark_node; + + if (except_stmt != NULL_TREE) + try_stmt = build2_loc (location.gcc_location (), TRY_CATCH_EXPR, + void_type_node, try_stmt, + build2_loc (location.gcc_location (), CATCH_EXPR, + void_type_node, NULL, except_stmt)); + if (finally_stmt != NULL_TREE) + try_stmt = build2_loc (location.gcc_location (), TRY_FINALLY_EXPR, + void_type_node, try_stmt, finally_stmt); + return try_stmt; +} + +// If. + +tree +Gcc_backend::if_statement (tree, tree cond_tree, tree then_tree, tree else_tree, + Location location) +{ + if (cond_tree == error_mark_node || then_tree == error_mark_node + || else_tree == error_mark_node) + return error_mark_node; + tree ret = build3_loc (location.gcc_location (), COND_EXPR, void_type_node, + cond_tree, then_tree, else_tree); + return ret; +} + +// Loops + +tree +Gcc_backend::loop_expression (tree body, Location locus) +{ + return fold_build1_loc (locus.gcc_location (), LOOP_EXPR, void_type_node, + body); +} + +tree +Gcc_backend::exit_expression (tree cond_tree, Location locus) +{ + return fold_build1_loc (locus.gcc_location (), EXIT_EXPR, void_type_node, + cond_tree); +} + +// Pair of statements. + +tree +Gcc_backend::compound_statement (tree s1, tree s2) +{ + tree stmt_list = NULL_TREE; + tree t = s1; + if (t == error_mark_node) + return error_mark_node; + append_to_statement_list (t, &stmt_list); + t = s2; + if (t == error_mark_node) + return error_mark_node; + append_to_statement_list (t, &stmt_list); + + // If neither statement has any side effects, stmt_list can be NULL + // at this point. + if (stmt_list == NULL_TREE) + stmt_list = integer_zero_node; + + return stmt_list; +} + +// List of statements. + +tree +Gcc_backend::statement_list (const std::vector &statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector::const_iterator p = statements.begin (); + p != statements.end (); ++p) + { + tree t = (*p); + if (t == error_mark_node) + return error_mark_node; + append_to_statement_list (t, &stmt_list); + } + return stmt_list; +} + +// Make a block. For some reason gcc uses a dual structure for +// blocks: BLOCK tree nodes and BIND_EXPR tree nodes. Since the +// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in +// the Bblock. + +tree +Gcc_backend::block (tree fndecl, tree enclosing, + const std::vector &vars, + Location start_location, Location) +{ + tree block_tree = make_node (BLOCK); + if (enclosing == NULL) + { + gcc_assert (fndecl != NULL_TREE); + + // We may have already created a block for local variables when + // we take the address of a parameter. + if (DECL_INITIAL (fndecl) == NULL_TREE) + { + BLOCK_SUPERCONTEXT (block_tree) = fndecl; + DECL_INITIAL (fndecl) = block_tree; + } + else + { + tree superblock_tree = DECL_INITIAL (fndecl); + BLOCK_SUPERCONTEXT (block_tree) = superblock_tree; + tree *pp; + for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE; + pp = &BLOCK_CHAIN (*pp)) + ; + *pp = block_tree; + } + } + else + { + tree superblock_tree = BIND_EXPR_BLOCK (enclosing); + gcc_assert (TREE_CODE (superblock_tree) == BLOCK); + + BLOCK_SUPERCONTEXT (block_tree) = superblock_tree; + tree *pp; + for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE; + pp = &BLOCK_CHAIN (*pp)) + ; + *pp = block_tree; + } + + tree *pp = &BLOCK_VARS (block_tree); + for (std::vector::const_iterator pv = vars.begin (); + pv != vars.end (); ++pv) + { + *pp = (*pv)->get_decl (); + if (*pp != error_mark_node) + pp = &DECL_CHAIN (*pp); + } + *pp = NULL_TREE; + + TREE_USED (block_tree) = 1; + + tree bind_tree + = build3_loc (start_location.gcc_location (), BIND_EXPR, void_type_node, + BLOCK_VARS (block_tree), NULL_TREE, block_tree); + TREE_SIDE_EFFECTS (bind_tree) = 1; + return bind_tree; +} + +// Add statements to a block. + +void +Gcc_backend::block_add_statements (tree bind_tree, + const std::vector &statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector::const_iterator p = statements.begin (); + p != statements.end (); ++p) + { + tree s = (*p); + if (s != error_mark_node) + append_to_statement_list (s, &stmt_list); + } + + gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR); + BIND_EXPR_BODY (bind_tree) = stmt_list; +} + +// This is not static because we declare it with GTY(()) in rust-c.h. +tree rust_non_zero_struct; + +// Return a type corresponding to TYPE with non-zero size. + +tree +Gcc_backend::non_zero_size_type (tree type) +{ + if (int_size_in_bytes (type) != 0) + return type; + + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + if (TYPE_FIELDS (type) != NULL_TREE) + { + tree ns = make_node (RECORD_TYPE); + tree field_trees = NULL_TREE; + tree *pp = &field_trees; + for (tree field = TYPE_FIELDS (type); field != NULL_TREE; + field = DECL_CHAIN (field)) + { + tree ft = TREE_TYPE (field); + if (field == TYPE_FIELDS (type)) + ft = non_zero_size_type (ft); + tree f = build_decl (DECL_SOURCE_LOCATION (field), FIELD_DECL, + DECL_NAME (field), ft); + DECL_CONTEXT (f) = ns; + *pp = f; + pp = &DECL_CHAIN (f); + } + TYPE_FIELDS (ns) = field_trees; + layout_type (ns); + return ns; + } + + if (rust_non_zero_struct == NULL_TREE) + { + type = make_node (RECORD_TYPE); + tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier ("dummy"), boolean_type_node); + DECL_CONTEXT (field) = type; + TYPE_FIELDS (type) = field; + layout_type (type); + rust_non_zero_struct = type; + } + return rust_non_zero_struct; + + case ARRAY_TYPE: { + tree element_type = non_zero_size_type (TREE_TYPE (type)); + return build_array_type_nelts (element_type, 1); + } + + default: + gcc_unreachable (); + } + + gcc_unreachable (); +} + +// Convert EXPR_TREE to TYPE_TREE. Sometimes the same unnamed Rust type +// can be created multiple times and thus have multiple tree +// representations. Make sure this does not confuse the middle-end. + +tree +Gcc_backend::convert_tree (tree type_tree, tree expr_tree, Location location) +{ + if (type_tree == TREE_TYPE (expr_tree)) + return expr_tree; + + if (type_tree == error_mark_node || expr_tree == error_mark_node + || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + if (POINTER_TYPE_P (type_tree) || INTEGRAL_TYPE_P (type_tree) + || SCALAR_FLOAT_TYPE_P (type_tree) || COMPLEX_FLOAT_TYPE_P (type_tree)) + return fold_convert_loc (location.gcc_location (), type_tree, expr_tree); + else if (TREE_CODE (type_tree) == RECORD_TYPE + || TREE_CODE (type_tree) == UNION_TYPE + || TREE_CODE (type_tree) == ARRAY_TYPE) + { + gcc_assert (int_size_in_bytes (type_tree) + == int_size_in_bytes (TREE_TYPE (expr_tree))); + if (TYPE_MAIN_VARIANT (type_tree) + == TYPE_MAIN_VARIANT (TREE_TYPE (expr_tree))) + return fold_build1_loc (location.gcc_location (), NOP_EXPR, type_tree, + expr_tree); + return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR, + type_tree, expr_tree); + } + + gcc_unreachable (); +} + +// Make a global variable. + +Bvariable * +Gcc_backend::global_variable (const std::string &var_name, + const std::string &asm_name, tree type_tree, + bool is_external, bool is_hidden, + bool in_unique_section, Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + + // The GNU linker does not like dynamic variables with zero size. + tree orig_type_tree = type_tree; + if ((is_external || !is_hidden) && int_size_in_bytes (type_tree) == 0) + type_tree = this->non_zero_size_type (type_tree); + + tree decl = build_decl (location.gcc_location (), VAR_DECL, + get_identifier_from_string (var_name), type_tree); + if (is_external) + DECL_EXTERNAL (decl) = 1; + else + TREE_STATIC (decl) = 1; + if (!is_hidden) + { + TREE_PUBLIC (decl) = 1; + SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name)); + } + else + { + SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name)); + } + + TREE_USED (decl) = 1; + + if (in_unique_section) + resolve_unique_section (decl, 0, 1); + + rust_preserve_from_gc (decl); + + return new Bvariable (decl, orig_type_tree); +} + +// Set the initial value of a global variable. + +void +Gcc_backend::global_variable_set_init (Bvariable *var, tree expr_tree) +{ + if (expr_tree == error_mark_node) + return; + gcc_assert (TREE_CONSTANT (expr_tree)); + tree var_decl = var->get_decl (); + if (var_decl == error_mark_node) + return; + DECL_INITIAL (var_decl) = expr_tree; + + // If this variable goes in a unique section, it may need to go into + // a different one now that DECL_INITIAL is set. + if (symtab_node::get (var_decl) + && symtab_node::get (var_decl)->implicit_section) + { + set_decl_section_name (var_decl, (const char *) NULL); + resolve_unique_section (var_decl, compute_reloc_for_constant (expr_tree), + 1); + } +} + +// Make a local variable. + +Bvariable * +Gcc_backend::local_variable (tree function, const std::string &name, + tree type_tree, Bvariable *decl_var, + Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + tree decl = build_decl (location.gcc_location (), VAR_DECL, + get_identifier_from_string (name), type_tree); + DECL_CONTEXT (decl) = function; + + if (decl_var != NULL) + { + DECL_HAS_VALUE_EXPR_P (decl) = 1; + SET_DECL_VALUE_EXPR (decl, decl_var->get_decl ()); + } + rust_preserve_from_gc (decl); + return new Bvariable (decl); +} + +// Make a function parameter variable. + +Bvariable * +Gcc_backend::parameter_variable (tree function, const std::string &name, + tree type_tree, Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + tree decl = build_decl (location.gcc_location (), PARM_DECL, + get_identifier_from_string (name), type_tree); + DECL_CONTEXT (decl) = function; + DECL_ARG_TYPE (decl) = type_tree; + + rust_preserve_from_gc (decl); + return new Bvariable (decl); +} + +// Make a static chain variable. + +Bvariable * +Gcc_backend::static_chain_variable (tree fndecl, const std::string &name, + tree type_tree, Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + tree decl = build_decl (location.gcc_location (), PARM_DECL, + get_identifier_from_string (name), type_tree); + DECL_CONTEXT (decl) = fndecl; + DECL_ARG_TYPE (decl) = type_tree; + TREE_USED (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + TREE_READONLY (decl) = 1; + + struct function *f = DECL_STRUCT_FUNCTION (fndecl); + if (f == NULL) + { + push_struct_function (fndecl); + pop_cfun (); + f = DECL_STRUCT_FUNCTION (fndecl); + } + gcc_assert (f->static_chain_decl == NULL); + f->static_chain_decl = decl; + DECL_STATIC_CHAIN (fndecl) = 1; + + rust_preserve_from_gc (decl); + return new Bvariable (decl); +} + +// Make a temporary variable. + +Bvariable * +Gcc_backend::temporary_variable (tree fndecl, tree bind_tree, tree type_tree, + tree init_tree, bool is_address_taken, + Location location, tree *pstatement) +{ + gcc_assert (fndecl != NULL_TREE); + if (type_tree == error_mark_node || init_tree == error_mark_node + || fndecl == error_mark_node) + { + *pstatement = error_mark_node; + return this->error_variable (); + } + + tree var; + // We can only use create_tmp_var if the type is not addressable. + if (!TREE_ADDRESSABLE (type_tree)) + { + if (DECL_STRUCT_FUNCTION (fndecl) == NULL) + push_struct_function (fndecl); + else + push_cfun (DECL_STRUCT_FUNCTION (fndecl)); + + var = create_tmp_var (type_tree, "RUSTTMP"); + pop_cfun (); + } + else + { + gcc_assert (bind_tree != NULL_TREE); + var = build_decl (location.gcc_location (), VAR_DECL, + create_tmp_var_name ("RUSTTMP"), type_tree); + DECL_ARTIFICIAL (var) = 1; + DECL_IGNORED_P (var) = 1; + TREE_USED (var) = 1; + DECL_CONTEXT (var) = fndecl; + + // We have to add this variable to the BLOCK and the BIND_EXPR. + gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR); + tree block_tree = BIND_EXPR_BLOCK (bind_tree); + gcc_assert (TREE_CODE (block_tree) == BLOCK); + DECL_CHAIN (var) = BLOCK_VARS (block_tree); + BLOCK_VARS (block_tree) = var; + BIND_EXPR_VARS (bind_tree) = BLOCK_VARS (block_tree); + } + + if (this->type_size (type_tree) != 0 && init_tree != NULL_TREE + && TREE_TYPE (init_tree) != void_type_node) + DECL_INITIAL (var) = this->convert_tree (type_tree, init_tree, location); + + if (is_address_taken) + TREE_ADDRESSABLE (var) = 1; + + *pstatement + = build1_loc (location.gcc_location (), DECL_EXPR, void_type_node, var); + + // For a zero sized type, don't initialize VAR with BINIT, but still + // evaluate BINIT for its side effects. + if (init_tree != NULL_TREE + && (this->type_size (type_tree) == 0 + || TREE_TYPE (init_tree) == void_type_node)) + *pstatement = this->compound_statement (init_tree, *pstatement); + + return new Bvariable (var); +} + +// Make a label. + +tree +Gcc_backend::label (tree func_tree, const std::string &name, Location location) +{ + tree decl; + if (name.empty ()) + { + if (DECL_STRUCT_FUNCTION (func_tree) == NULL) + push_struct_function (func_tree); + else + push_cfun (DECL_STRUCT_FUNCTION (func_tree)); + + decl = create_artificial_label (location.gcc_location ()); + + pop_cfun (); + } + else + { + tree id = get_identifier_from_string (name); + decl + = build_decl (location.gcc_location (), LABEL_DECL, id, void_type_node); + DECL_CONTEXT (decl) = func_tree; + } + return decl; +} + +// Make a statement which defines a label. + +tree +Gcc_backend::label_definition_statement (tree label) +{ + return fold_build1_loc (DECL_SOURCE_LOCATION (label), LABEL_EXPR, + void_type_node, label); +} + +// Make a goto statement. + +tree +Gcc_backend::goto_statement (tree label, Location location) +{ + return fold_build1_loc (location.gcc_location (), GOTO_EXPR, void_type_node, + label); +} + +// Get the address of a label. + +tree +Gcc_backend::label_address (tree label, Location location) +{ + TREE_USED (label) = 1; + TREE_ADDRESSABLE (label) = 1; + tree ret + = fold_convert_loc (location.gcc_location (), ptr_type_node, + build_fold_addr_expr_loc (location.gcc_location (), + label)); + return ret; +} + +// Declare or define a new function. + +tree +Gcc_backend::function (tree functype, const std::string &name, + const std::string &asm_name, unsigned int flags, + Location location) +{ + if (functype != error_mark_node) + { + gcc_assert (FUNCTION_POINTER_TYPE_P (functype)); + functype = TREE_TYPE (functype); + } + tree id = get_identifier_from_string (name); + if (functype == error_mark_node || id == error_mark_node) + return error_mark_node; + + tree decl + = build_decl (location.gcc_location (), FUNCTION_DECL, id, functype); + if (!asm_name.empty ()) + SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name)); + + if ((flags & function_is_declaration) != 0) + DECL_EXTERNAL (decl) = 1; + else + { + tree restype = TREE_TYPE (functype); + tree resdecl = build_decl (location.gcc_location (), RESULT_DECL, + NULL_TREE, restype); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_CONTEXT (resdecl) = decl; + DECL_RESULT (decl) = resdecl; + } + if ((flags & function_is_uninlinable) != 0) + DECL_UNINLINABLE (decl) = 1; + if ((flags & function_does_not_return) != 0) + TREE_THIS_VOLATILE (decl) = 1; + if ((flags & function_in_unique_section) != 0) + resolve_unique_section (decl, 0, 1); + + rust_preserve_from_gc (decl); + return decl; +} + +// Create a statement that runs all deferred calls for FUNCTION. This should +// be a statement that looks like this in C++: +// finish: +// try { UNDEFER; } catch { CHECK_DEFER; goto finish; } + +tree +Gcc_backend::function_defer_statement (tree function, tree undefer_tree, + tree defer_tree, Location location) +{ + if (undefer_tree == error_mark_node || defer_tree == error_mark_node + || function == error_mark_node) + return error_mark_node; + + if (DECL_STRUCT_FUNCTION (function) == NULL) + push_struct_function (function); + else + push_cfun (DECL_STRUCT_FUNCTION (function)); + + tree stmt_list = NULL; + tree label = this->label (function, "", location); + tree label_def = this->label_definition_statement (label); + append_to_statement_list (label_def, &stmt_list); + + tree jump_stmt = this->goto_statement (label, location); + tree catch_body + = build2 (COMPOUND_EXPR, void_type_node, defer_tree, jump_stmt); + catch_body = build2 (CATCH_EXPR, void_type_node, NULL, catch_body); + tree try_catch + = build2 (TRY_CATCH_EXPR, void_type_node, undefer_tree, catch_body); + append_to_statement_list (try_catch, &stmt_list); + pop_cfun (); + + return stmt_list; +} + +// Record PARAM_VARS as the variables to use for the parameters of FUNCTION. +// This will only be called for a function definition. + +bool +Gcc_backend::function_set_parameters ( + tree function, const std::vector ¶m_vars) +{ + if (function == error_mark_node) + return false; + + tree params = NULL_TREE; + tree *pp = ¶ms; + for (std::vector::const_iterator pv = param_vars.begin (); + pv != param_vars.end (); ++pv) + { + *pp = (*pv)->get_decl (); + gcc_assert (*pp != error_mark_node); + pp = &DECL_CHAIN (*pp); + } + *pp = NULL_TREE; + DECL_ARGUMENTS (function) = params; + return true; +} + +// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS, +// FUNCTION_DECLS, and VARIABLE_DECLS declared globally, as well as +// emit early debugging information. + +void +Gcc_backend::write_global_definitions ( + const std::vector &type_decls, const std::vector &constant_decls, + const std::vector &function_decls, + const std::vector &variable_decls) +{ + size_t count_definitions = type_decls.size () + constant_decls.size () + + function_decls.size () + variable_decls.size (); + + tree *defs = new tree[count_definitions]; + + // Convert all non-erroneous declarations into Gimple form. + size_t i = 0; + for (std::vector::const_iterator p = variable_decls.begin (); + p != variable_decls.end (); ++p) + { + tree v = (*p)->get_decl (); + if (v != error_mark_node) + { + defs[i] = v; + rust_preserve_from_gc (defs[i]); + ++i; + } + } + + for (std::vector::const_iterator p = type_decls.begin (); + p != type_decls.end (); ++p) + { + tree type_tree = (*p); + if (type_tree != error_mark_node && IS_TYPE_OR_DECL_P (type_tree)) + { + defs[i] = TYPE_NAME (type_tree); + gcc_assert (defs[i] != NULL); + rust_preserve_from_gc (defs[i]); + ++i; + } + } + for (std::vector::const_iterator p = constant_decls.begin (); + p != constant_decls.end (); ++p) + { + if ((*p) != error_mark_node) + { + defs[i] = (*p); + rust_preserve_from_gc (defs[i]); + ++i; + } + } + for (std::vector::const_iterator p = function_decls.begin (); + p != function_decls.end (); ++p) + { + tree decl = (*p); + if (decl != error_mark_node) + { + rust_preserve_from_gc (decl); + if (DECL_STRUCT_FUNCTION (decl) == NULL) + allocate_struct_function (decl, false); + dump_function (TDI_original, decl); + cgraph_node::finalize_function (decl, true); + + defs[i] = decl; + ++i; + } + } + + // Pass everything back to the middle-end. + + wrapup_global_declarations (defs, i); + + delete[] defs; +} + +void +Gcc_backend::write_export_data (const char *bytes, unsigned int size) +{ + rust_write_export_data (bytes, size); +} + +// Return the backend generator. + +Backend * +rust_get_backend () +{ + return new Gcc_backend (); +}