diff --git a/src/doc/unstable-book/src/language-features/c-ffi-const.md b/src/doc/unstable-book/src/language-features/c-ffi-const.md
new file mode 100644
index 0000000000000..55b4e8f27f8d9
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/c-ffi-const.md
@@ -0,0 +1,51 @@
+# `c_ffi_const`
+
+The `#[c_ffi_const]` attribute applies clang's `const` attribute to foreign
+functions declarations. 
+
+That is, `#[c_ffi_const]` functions shall have no effects except for its return
+value, which can only depend on the values of the function parameters, and is
+not affected by changes to the observable state of the program.
+
+The behavior of calling a `#[c_ffi_const]` function that violates these
+requirements is undefined.
+
+This attribute enables Rust to perform common optimizations, like sub-expression
+elimination, and it can avoid emitting some calls in repeated invocations of the
+function with the same argument values regardless of other operations being
+performed in between these functions calls (as opposed to `#[c_ffi_pure]`
+functions).
+
+## Pitfalls
+
+A `#[c_ffi_const]` function can only read global memory that would not affect
+its return value for the whole execution of the program (e.g. immutable global
+memory). `#[c_ffi_const]` functions are referentially-transparent and therefore
+more strict than `#[c_ffi_pure]` functions.
+
+A common pitfall involves applying the `#[c_ffi_const]` attribute to a
+function that reads memory through pointer arguments which do not necessarily
+point to immutable global memory.
+
+A `#[c_ffi_const]` function that returns unit has no effect on the abstract
+machine's state, and a `#[c_ffi_const]` function cannot be `#[c_ffi_pure]`.
+
+A diverging and C or C++ `const` function is unreachable. Diverging via a
+side-effect (e.g. a call to `abort`) violates `const` pre-conditions. Divergence
+without side-effects is undefined behavior in C++ and not possible in C. In C++,
+the behavior of infinite loops without side-effects is undefined, while in C
+these loops can be assumed to terminate. This would cause a diverging function
+to return, invoking undefined behavior.
+
+When translating C headers to Rust FFI, it is worth verifying for which targets
+the `const` attribute is enabled in those headers, and using the appropriate
+`cfg` macros in the Rust side to match those definitions. While the semantics of
+`const` are implemented identically by many C and C++ compilers, e.g., clang,
+[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
+implemented in this way on all of them. It is therefore also worth verifying
+that the semantics of the C toolchain used to compile the binary being linked
+against are compatible with those of the `#[c_ffi_const]`.
+
+[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html
+[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute
+[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm
diff --git a/src/doc/unstable-book/src/language-features/c-ffi-pure.md b/src/doc/unstable-book/src/language-features/c-ffi-pure.md
new file mode 100644
index 0000000000000..c8fa118c29853
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/c-ffi-pure.md
@@ -0,0 +1,55 @@
+# `c_ffi_pure`
+
+The `#[c_ffi_pure]` attribute applies clang's `pure` attribute to foreign
+functions declarations. 
+
+That is, `#[c_ffi_pure]` functions shall have no effects except for its return
+value, which shall not change across two consecutive function calls and can only
+depend on the values of the function parameters and/or global memory.
+
+The behavior of calling a `#[c_ffi_pure]` function that violates these
+requirements is undefined.
+
+This attribute enables Rust to perform common optimizations, like sub-expression
+elimination and loop optimizations. Some common examples of pure functions are
+`strlen` or `memcmp`.
+
+These optimizations only apply across successive invocations of the function,
+since any other function could modify global memory read by `#[c_ffi_pure]`
+functions, altering their result. The `#[c_ffi_const]` attribute allows
+sub-expression elimination regardless of any operations in between the function
+calls.
+
+## Pitfalls
+
+A `#[c_ffi_pure]` function can read global memory through the function
+parameters (e.g. pointers), globals, etc. `#[c_ffi_pure]` functions are not
+referentially-transparent, and are therefore more relaxed than `#[c_ffi_const]`
+functions.
+
+However, accesing global memory through volatile or atomic reads can violate the
+requirement that two consecutive function calls shall return the same value.
+
+A `pure` function that returns unit has no effect on the abstract machine's
+state.
+
+A diverging and `pure` C or C++ function is unreachable. Diverging via a
+side-effect (e.g. a call to `abort`) violates `pure` requirements. Divergence
+without side-effects is undefined behavior in C++ and not possible in C. In C++,
+the behavior of infinite loops without side-effects is undefined, while in C
+these loops can be assumed to terminate. This would cause a diverging function
+to return, invoking undefined behavior.
+
+When translating C headers to Rust FFI, it is worth verifying for which targets
+the `pure` attribute is enabled in those headers, and using the appropriate
+`cfg` macros in the Rust side to match those definitions. While the semantics of
+`pure` are implemented identically by many C and C++ compilers, e.g., clang,
+[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
+implemented in this way on all of them. It is therefore also worth verifying
+that the semantics of the C toolchain used to compile the binary being linked
+against are compatible with those of the `#[c_ffi_pure]`.
+
+
+[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html
+[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
+[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 3e7dd1432e1e3..ae227ec9eae52 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -2489,6 +2489,12 @@ bitflags! {
         /// #[used], indicates that LLVM can't eliminate this function (but the
         /// linker can!)
         const USED                      = 1 << 9;
+        /// #[c_ffi_pure]: applies clang's `pure` attribute to a foreign function
+        /// declaration.
+        const C_FFI_PURE = 1 << 10;
+        /// #[c_ffi_const]: applies clang's `const` attribute to a foreign function
+        /// declaration.
+        const C_FFI_CONST = 1 << 11;
     }
 }
 
diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs
index e6bc7bca46bc9..0dabfd8e8aef4 100644
--- a/src/librustc_codegen_llvm/attributes.rs
+++ b/src/librustc_codegen_llvm/attributes.rs
@@ -223,6 +223,12 @@ pub fn from_fn_attrs(
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
         Attribute::Cold.apply_llfn(Function, llfn);
     }
+    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::C_FFI_PURE) {
+        Attribute::ReadOnly.apply_llfn(Function, llfn);
+    }
+    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::C_FFI_CONST) {
+        Attribute::ReadNone.apply_llfn(Function, llfn);
+    }
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
         naked(llfn, true);
     }
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index 58bdfc47fcaed..569850e350eb3 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -116,6 +116,7 @@ pub enum Attribute {
     SanitizeMemory  = 22,
     NonLazyBind     = 23,
     OptimizeNone    = 24,
+    ReadNone        = 25,
 }
 
 /// LLVMIntPredicate
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 9dc74c5d63a4e..3cb0f9325add4 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -2270,7 +2270,41 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
         } else if attr.check_name("unwind") {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND;
-        } else if attr.check_name("rustc_allocator_nounwind") {
+        } else if attr.check_name("c_ffi_pure") {
+            if tcx.is_foreign_item(id) {
+                if attrs.iter().any(|a| a.check_name("c_ffi_const")) {
+                    // `#[c_ffi_const]` functions cannot be `#[c_ffi_pure]`
+                    struct_span_err!(
+                        tcx.sess,
+                        attr.span,
+                        E0726,
+                        "`#[c_ffi_const]` function cannot be`#[c_ffi_pure]`"
+                    ).emit();
+                } else {
+                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::C_FFI_PURE;
+                }
+            } else {
+                // `#[c_ffi_pure]` is only allowed on foreign functions
+                struct_span_err!(
+                    tcx.sess,
+                    attr.span,
+                    E0724,
+                    "`#[c_ffi_pure]` may only be used on foreign functions"
+                ).emit();
+            }
+        } else if attr.check_name("c_ffi_const") {
+            if tcx.is_foreign_item(id) {
+                codegen_fn_attrs.flags |= CodegenFnAttrFlags::C_FFI_CONST;
+            } else {
+                // `#[c_ffi_const]` is only allowed on foreign functions
+                struct_span_err!(
+                    tcx.sess,
+                    attr.span,
+                    E0725,
+                    "`#[c_ffi_const]` may only be used on foreign functions"
+                ).emit();
+            }
+      } else if attr.check_name("rustc_allocator_nounwind") {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
         } else if attr.check_name("naked") {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 3ed09dfe99239..baf756ae44fcd 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -4720,4 +4720,7 @@ register_diagnostics! {
     E0698, // type inside generator must be known in this context
     E0719, // duplicate values for associated type binding
     E0722, // Malformed #[optimize] attribute
+    E0724, // `#[c_ffi_pure]` is only allowed on foreign functions
+    E0725, // `#[c_ffi_const]` is only allowed on foreign functions
+    E0726, // `#[c_ffi_const]` functions cannot be `#[c_ffi_pure]`
 }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index e7b9a884b5e0c..e97a498205950 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -290,6 +290,12 @@ declare_features! (
     // The `repr(i128)` annotation for enums.
     (active, repr128, "1.16.0", Some(35118), None),
 
+    // Allows the use of `#[c_ffi_pure]` on foreign functions.
+    (active, c_ffi_pure, "1.34.0", Some(58329), None),
+
+    // Allows the use of `#[c_ffi_const]` on foreign functions.
+    (active, c_ffi_const, "1.34.0", Some(58328), None),
+
     // The `unadjusted` ABI; perma-unstable.
     //
     // rustc internal
@@ -1124,6 +1130,16 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
                                  "the `#[naked]` attribute \
                                   is an experimental feature",
                                  cfg_fn!(naked_functions))),
+    ("c_ffi_pure", Whitelisted, template!(Word), Gated(Stability::Unstable,
+                                                     "c_ffi_pure",
+                                                     "the `#[c_ffi_pure]` attribute \
+                                                      is an experimental feature",
+                                                     cfg_fn!(c_ffi_pure))),
+    ("c_ffi_const", Whitelisted, template!(Word), Gated(Stability::Unstable,
+                                                      "c_ffi_const",
+                                                      "the `#[c_ffi_const]` attribute \
+                                                       is an experimental feature",
+                                                      cfg_fn!(c_ffi_const))),
     ("target_feature", Whitelisted, template!(List: r#"enable = "name""#), Ungated),
     ("export_name", Whitelisted, template!(NameValueStr: "name"), Ungated),
     ("inline", Whitelisted, template!(Word, List: "always|never"), Ungated),
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index b33165b846339..c87b78ba4d5e3 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -170,6 +170,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
     return Attribute::OptimizeForSize;
   case ReadOnly:
     return Attribute::ReadOnly;
+  case ReadNone:
+    return Attribute::ReadNone;
   case SExt:
     return Attribute::SExt;
   case StructRet:
diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h
index 933266b402526..2ea57e46bcd45 100644
--- a/src/rustllvm/rustllvm.h
+++ b/src/rustllvm/rustllvm.h
@@ -85,6 +85,7 @@ enum LLVMRustAttribute {
   SanitizeMemory = 22,
   NonLazyBind = 23,
   OptimizeNone = 24,
+  ReadNone = 25,
 };
 
 typedef struct OpaqueRustString *RustStringRef;
diff --git a/src/test/codegen/c-ffi-const.rs b/src/test/codegen/c-ffi-const.rs
new file mode 100644
index 0000000000000..1f60a965f92e9
--- /dev/null
+++ b/src/test/codegen/c-ffi-const.rs
@@ -0,0 +1,12 @@
+// compile-flags: -C no-prepopulate-passes
+#![crate_type = "lib"]
+#![feature(c_ffi_const)]
+
+pub fn bar() { unsafe { foo() } }
+
+extern {
+    // CHECK-LABEL: declare void @foo()
+    // CHECK-SAME: [[ATTRS:#[0-9]+]]
+    // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}readnone{{.*}} }
+    #[c_ffi_const] pub fn foo();
+}
diff --git a/src/test/codegen/c-ffi-pure.rs b/src/test/codegen/c-ffi-pure.rs
new file mode 100644
index 0000000000000..e60d88f63e33f
--- /dev/null
+++ b/src/test/codegen/c-ffi-pure.rs
@@ -0,0 +1,12 @@
+// compile-flags: -C no-prepopulate-passes
+#![crate_type = "lib"]
+#![feature(c_ffi_pure)]
+
+pub fn bar() { unsafe { foo() } }
+
+extern {
+    // CHECK-LABEL: declare void @foo()
+    // CHECK-SAME: [[ATTRS:#[0-9]+]]
+    // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}readonly{{.*}} }
+    #[c_ffi_pure] pub fn foo();
+}
diff --git a/src/test/ui/c_ffi_const.rs b/src/test/ui/c_ffi_const.rs
new file mode 100644
index 0000000000000..72e88acc4092c
--- /dev/null
+++ b/src/test/ui/c_ffi_const.rs
@@ -0,0 +1,6 @@
+// ignore-tidy-linelength
+#![feature(c_ffi_const, c_ffi_pure)]
+#![crate_type = "lib"]
+
+#[c_ffi_const] //~ ERROR `#[c_ffi_const]` may only be used on foreign functions [E0725]
+pub fn foo() {}
diff --git a/src/test/ui/c_ffi_const.stderr b/src/test/ui/c_ffi_const.stderr
new file mode 100644
index 0000000000000..9e6a3c75622ea
--- /dev/null
+++ b/src/test/ui/c_ffi_const.stderr
@@ -0,0 +1,9 @@
+error[E0725]: `#[c_ffi_const]` may only be used on foreign functions
+  --> $DIR/c_ffi_const.rs:5:1
+   |
+LL | #[c_ffi_const] //~ ERROR `#[c_ffi_const]` may only be used on foreign functions [E0725]
+   | ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0725`.
diff --git a/src/test/ui/c_ffi_const2.rs b/src/test/ui/c_ffi_const2.rs
new file mode 100644
index 0000000000000..dc1441d7203b5
--- /dev/null
+++ b/src/test/ui/c_ffi_const2.rs
@@ -0,0 +1,12 @@
+// ignore-tidy-linelength
+#![feature(c_ffi_const, c_ffi_pure)]
+
+extern {
+    #[c_ffi_pure] //~ ERROR `#[c_ffi_const]` function cannot be`#[c_ffi_pure]` [E0726]
+    #[c_ffi_const]
+    pub fn baz();
+}
+
+fn main() {
+    unsafe { baz() };
+}
diff --git a/src/test/ui/c_ffi_const2.stderr b/src/test/ui/c_ffi_const2.stderr
new file mode 100644
index 0000000000000..1fb1a2bb3a623
--- /dev/null
+++ b/src/test/ui/c_ffi_const2.stderr
@@ -0,0 +1,9 @@
+error[E0726]: `#[c_ffi_const]` function cannot be`#[c_ffi_pure]`
+  --> $DIR/c_ffi_const2.rs:5:5
+   |
+LL |     #[c_ffi_pure] //~ ERROR `#[c_ffi_const]` function cannot be`#[c_ffi_pure]` [E0726]
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0726`.
diff --git a/src/test/ui/c_ffi_pure.rs b/src/test/ui/c_ffi_pure.rs
new file mode 100644
index 0000000000000..63a5438365fd2
--- /dev/null
+++ b/src/test/ui/c_ffi_pure.rs
@@ -0,0 +1,6 @@
+// ignore-tidy-linelength
+#![feature(c_ffi_pure)]
+#![crate_type = "lib"]
+
+#[c_ffi_pure] //~ ERROR `#[c_ffi_pure]` may only be used on foreign functions [E0724]
+pub fn foo() {}
diff --git a/src/test/ui/c_ffi_pure.stderr b/src/test/ui/c_ffi_pure.stderr
new file mode 100644
index 0000000000000..c7c2c063312cb
--- /dev/null
+++ b/src/test/ui/c_ffi_pure.stderr
@@ -0,0 +1,9 @@
+error[E0724]: `#[c_ffi_pure]` may only be used on foreign functions
+  --> $DIR/c_ffi_pure.rs:5:1
+   |
+LL | #[c_ffi_pure] //~ ERROR `#[c_ffi_pure]` may only be used on foreign functions [E0724]
+   | ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0724`.
diff --git a/src/test/ui/feature-gates/feature-gate-c_ffi_const.rs b/src/test/ui/feature-gates/feature-gate-c_ffi_const.rs
new file mode 100644
index 0000000000000..b4c23c8589489
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-c_ffi_const.rs
@@ -0,0 +1,7 @@
+// ignore-tidy-linelength
+#![crate_type = "lib"]
+
+extern {
+    #[c_ffi_const] //~ ERROR the `#[c_ffi_const]` attribute is an experimental feature (see issue #58328)
+    pub fn foo();
+}
diff --git a/src/test/ui/feature-gates/feature-gate-c_ffi_const.stderr b/src/test/ui/feature-gates/feature-gate-c_ffi_const.stderr
new file mode 100644
index 0000000000000..a83c0251d4c8f
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-c_ffi_const.stderr
@@ -0,0 +1,11 @@
+error[E0658]: the `#[c_ffi_const]` attribute is an experimental feature (see issue #58328)
+  --> $DIR/feature-gate-c_ffi_const.rs:5:5
+   |
+LL |     #[c_ffi_const] //~ ERROR the `#[c_ffi_const]` attribute is an experimental feature (see issue #58328)
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(c_ffi_const)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-c_ffi_pure.rs b/src/test/ui/feature-gates/feature-gate-c_ffi_pure.rs
new file mode 100644
index 0000000000000..70f1e40443290
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-c_ffi_pure.rs
@@ -0,0 +1,7 @@
+// ignore-tidy-linelength
+#![crate_type = "lib"]
+
+extern {
+    #[c_ffi_pure] //~ ERROR the `#[c_ffi_pure]` attribute is an experimental feature (see issue #58329)
+    pub fn foo();
+}
diff --git a/src/test/ui/feature-gates/feature-gate-c_ffi_pure.stderr b/src/test/ui/feature-gates/feature-gate-c_ffi_pure.stderr
new file mode 100644
index 0000000000000..1545a6e63472d
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-c_ffi_pure.stderr
@@ -0,0 +1,11 @@
+error[E0658]: the `#[c_ffi_pure]` attribute is an experimental feature (see issue #58329)
+  --> $DIR/feature-gate-c_ffi_pure.rs:5:5
+   |
+LL |     #[c_ffi_pure] //~ ERROR the `#[c_ffi_pure]` attribute is an experimental feature (see issue #58329)
+   |     ^^^^^^^^^^^^^
+   |
+   = help: add #![feature(c_ffi_pure)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.