Merge remote-tracking branch 'aosp/master' into HEAD
This commit is contained in:
commit
959bc099a3
@ -20,12 +20,29 @@ LOCAL_PATH := $(call my-dir)
|
|||||||
# Benchmarks library, usable by projects outside this directory.
|
# Benchmarks library, usable by projects outside this directory.
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
benchmark_cflags := \
|
||||||
|
-O2 \
|
||||||
|
-fno-builtin \
|
||||||
|
-Wall \
|
||||||
|
-Wextra \
|
||||||
|
-Werror \
|
||||||
|
-Wunused \
|
||||||
|
|
||||||
|
benchmark_cppflags := \
|
||||||
|
-std=gnu++11 \
|
||||||
|
|
||||||
|
benchmarklib_src_files := \
|
||||||
|
Benchmark.cpp \
|
||||||
|
utils.cpp \
|
||||||
|
main.cpp \
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := libbenchmark
|
LOCAL_MODULE := libbenchmark
|
||||||
LOCAL_CFLAGS += -O2 -Wall -Wextra -Werror
|
LOCAL_CFLAGS := $(benchmark_cflags)
|
||||||
LOCAL_SRC_FILES := benchmark_main.cpp
|
LOCAL_CPPFLAGS := $(benchmark_cppflags)
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
LOCAL_SRC_FILES := $(benchmarklib_src_files)
|
||||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
LOCAL_C_INCLUDES := $(benchmark_c_includes)
|
||||||
|
LOCAL_STATIC_LIBRARIES := libbase
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
# Only supported on linux systems.
|
# Only supported on linux systems.
|
||||||
@ -33,11 +50,12 @@ ifeq ($(HOST_OS),linux)
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := libbenchmark
|
LOCAL_MODULE := libbenchmark
|
||||||
LOCAL_CFLAGS += -O2 -Wall -Wextra -Werror
|
LOCAL_CFLAGS := $(benchmark_cflags)
|
||||||
LOCAL_SRC_FILES := benchmark_main.cpp
|
LOCAL_CPPFLAGS := $(benchmark_cppflags)
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
LOCAL_SRC_FILES := $(benchmarklib_src_files)
|
||||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
LOCAL_C_INCLUDES := $(benchmark_c_includes)
|
||||||
LOCAL_MULTILIB := both
|
LOCAL_MULTILIB := both
|
||||||
|
LOCAL_STATIC_LIBRARIES := libbase
|
||||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
@ -45,16 +63,9 @@ endif
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Benchmarks.
|
# Benchmarks.
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
benchmark_src_files := \
|
||||||
benchmark_c_flags = \
|
|
||||||
-O2 \
|
|
||||||
-Wall -Wextra -Wunused \
|
|
||||||
-Werror \
|
|
||||||
-fno-builtin \
|
|
||||||
-std=gnu++11 \
|
|
||||||
|
|
||||||
benchmark_src_files = \
|
|
||||||
math_benchmark.cpp \
|
math_benchmark.cpp \
|
||||||
|
property_benchmark.cpp \
|
||||||
pthread_benchmark.cpp \
|
pthread_benchmark.cpp \
|
||||||
semaphore_benchmark.cpp \
|
semaphore_benchmark.cpp \
|
||||||
stdio_benchmark.cpp \
|
stdio_benchmark.cpp \
|
||||||
@ -70,9 +81,10 @@ LOCAL_MODULE := bionic-benchmarks
|
|||||||
LOCAL_MODULE_STEM_32 := bionic-benchmarks32
|
LOCAL_MODULE_STEM_32 := bionic-benchmarks32
|
||||||
LOCAL_MODULE_STEM_64 := bionic-benchmarks64
|
LOCAL_MODULE_STEM_64 := bionic-benchmarks64
|
||||||
LOCAL_MULTILIB := both
|
LOCAL_MULTILIB := both
|
||||||
LOCAL_CFLAGS += $(benchmark_c_flags)
|
LOCAL_CFLAGS := $(benchmark_cflags)
|
||||||
LOCAL_SRC_FILES := $(benchmark_src_files) property_benchmark.cpp
|
LOCAL_CPPFLAGS := $(benchmark_cppflags)
|
||||||
LOCAL_STATIC_LIBRARIES += libbenchmark
|
LOCAL_SRC_FILES := $(benchmark_src_files)
|
||||||
|
LOCAL_STATIC_LIBRARIES := libbenchmark libbase
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
# We don't build a static benchmark executable because it's not usually
|
# We don't build a static benchmark executable because it's not usually
|
||||||
@ -90,10 +102,11 @@ LOCAL_MODULE := bionic-benchmarks-glibc
|
|||||||
LOCAL_MODULE_STEM_32 := bionic-benchmarks-glibc32
|
LOCAL_MODULE_STEM_32 := bionic-benchmarks-glibc32
|
||||||
LOCAL_MODULE_STEM_64 := bionic-benchmarks-glibc64
|
LOCAL_MODULE_STEM_64 := bionic-benchmarks-glibc64
|
||||||
LOCAL_MULTILIB := both
|
LOCAL_MULTILIB := both
|
||||||
LOCAL_CFLAGS += $(benchmark_c_flags)
|
LOCAL_CFLAGS := $(benchmark_cflags)
|
||||||
LOCAL_LDFLAGS += -lrt
|
LOCAL_CPPFLAGS := $(benchmark_cppflags)
|
||||||
|
LOCAL_LDFLAGS := -lrt
|
||||||
LOCAL_SRC_FILES := $(benchmark_src_files)
|
LOCAL_SRC_FILES := $(benchmark_src_files)
|
||||||
LOCAL_STATIC_LIBRARIES += libbenchmark
|
LOCAL_STATIC_LIBRARIES := libbenchmark libbase
|
||||||
include $(BUILD_HOST_EXECUTABLE)
|
include $(BUILD_HOST_EXECUTABLE)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
159
benchmarks/Benchmark.cpp
Normal file
159
benchmarks/Benchmark.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <base/stringprintf.h>
|
||||||
|
|
||||||
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
static uint64_t NanoTime() {
|
||||||
|
struct timespec t;
|
||||||
|
t.tv_sec = t.tv_nsec = 0;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||||
|
return static_cast<uint64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Benchmark::header_printed_;
|
||||||
|
|
||||||
|
std::vector<Benchmark*>& Benchmark::List() {
|
||||||
|
static std::vector<Benchmark*> list;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Benchmark::MaxNameColumnWidth() {
|
||||||
|
size_t max = 20;
|
||||||
|
for (auto& benchmark : List()) {
|
||||||
|
max = std::max(max, benchmark->NameColumnWidth());
|
||||||
|
}
|
||||||
|
return static_cast<int>(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Benchmark::RunAll(std::vector<regex_t*>& regs) {
|
||||||
|
size_t benchmarks_run = 0;
|
||||||
|
header_printed_ = false;
|
||||||
|
for (auto& benchmark : List()) {
|
||||||
|
benchmarks_run += benchmark->RunAllArgs(regs);
|
||||||
|
}
|
||||||
|
return benchmarks_run;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Benchmark::PrintHeader() {
|
||||||
|
if (!header_printed_) {
|
||||||
|
printf("%-*s %10s %10s\n", MaxNameColumnWidth(), "", "iterations", "ns/op");
|
||||||
|
header_printed_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool BenchmarkT<T>::ShouldRun(std::vector<regex_t*>& regs, T arg) {
|
||||||
|
if (regs.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& re : regs) {
|
||||||
|
if (regexec(re, GetNameStr(arg).c_str(), 0, NULL, 0) != REG_NOMATCH) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Benchmark::StopBenchmarkTiming() {
|
||||||
|
if (start_time_ns_ != 0) {
|
||||||
|
total_time_ns_ += NanoTime() - start_time_ns_;
|
||||||
|
}
|
||||||
|
start_time_ns_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Benchmark::StartBenchmarkTiming() {
|
||||||
|
if (start_time_ns_ == 0) {
|
||||||
|
start_time_ns_ = NanoTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BenchmarkWithoutArg::GetNameStr(void*) {
|
||||||
|
return Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string BenchmarkWithArg<int>::GetNameStr(int arg) {
|
||||||
|
return Name() + "/" + PrettyInt(arg, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string BenchmarkWithArg<double>::GetNameStr(double arg) {
|
||||||
|
return Name() + "/" + android::base::StringPrintf("%0.6f", arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void BenchmarkT<T>::RunWithArg(T arg) {
|
||||||
|
int new_iterations = 1;
|
||||||
|
int iterations;
|
||||||
|
while (new_iterations < 1e8) {
|
||||||
|
bytes_processed_ = 0;
|
||||||
|
total_time_ns_ = 0;
|
||||||
|
start_time_ns_ = 0;
|
||||||
|
|
||||||
|
iterations = new_iterations;
|
||||||
|
RunIterations(iterations, arg);
|
||||||
|
if (total_time_ns_ >= 1e9) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_time_ns_/iterations == 0) {
|
||||||
|
new_iterations = 1e9;
|
||||||
|
} else {
|
||||||
|
new_iterations = 1e9/ (total_time_ns_/iterations);
|
||||||
|
}
|
||||||
|
new_iterations = std::max(iterations + 1,
|
||||||
|
std::min(new_iterations + new_iterations/2, 100*iterations));
|
||||||
|
|
||||||
|
new_iterations = Round(new_iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%-*s %10s %10" PRId64, MaxNameColumnWidth(), GetNameStr(arg).c_str(),
|
||||||
|
PrettyInt(iterations, 10).c_str(), total_time_ns_/iterations);
|
||||||
|
|
||||||
|
if (total_time_ns_ > 0 && bytes_processed_ > 0) {
|
||||||
|
double gib_processed = static_cast<double>(bytes_processed_)/1e9;
|
||||||
|
double seconds = static_cast<double>(total_time_ns_)/1e9;
|
||||||
|
printf(" %8.3f GiB/s", gib_processed/seconds);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
template class BenchmarkT<int>;
|
||||||
|
template class BenchmarkT<double>;
|
||||||
|
template class BenchmarkT<void*>;
|
||||||
|
|
||||||
|
template class BenchmarkWithArg<int>;
|
||||||
|
template class BenchmarkWithArg<double>;
|
||||||
|
|
||||||
|
} // namespace testing
|
169
benchmarks/benchmark/Benchmark.h
Normal file
169
benchmarks/benchmark/Benchmark.h
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BENCHMARKS_BENCHMARK_H_
|
||||||
|
#define BENCHMARKS_BENCHMARK_H_
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
class Benchmark {
|
||||||
|
public:
|
||||||
|
Benchmark() {
|
||||||
|
List().push_back(this);
|
||||||
|
}
|
||||||
|
virtual ~Benchmark() {}
|
||||||
|
|
||||||
|
virtual std::string Name() = 0;
|
||||||
|
|
||||||
|
virtual size_t RunAllArgs(std::vector<regex_t*>&) = 0;
|
||||||
|
|
||||||
|
void SetBenchmarkBytesProcessed(uint64_t bytes) { bytes_processed_ += bytes; }
|
||||||
|
void StopBenchmarkTiming();
|
||||||
|
void StartBenchmarkTiming();
|
||||||
|
|
||||||
|
// Run all of the benchmarks that have registered.
|
||||||
|
static size_t RunAll(std::vector<regex_t*>&);
|
||||||
|
|
||||||
|
static std::vector<Benchmark*>& List();
|
||||||
|
|
||||||
|
static int MaxNameColumnWidth();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual size_t NameColumnWidth() = 0;
|
||||||
|
|
||||||
|
uint64_t bytes_processed_;
|
||||||
|
uint64_t total_time_ns_;
|
||||||
|
uint64_t start_time_ns_;
|
||||||
|
|
||||||
|
static bool header_printed_;
|
||||||
|
|
||||||
|
static void PrintHeader();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class BenchmarkT : public Benchmark {
|
||||||
|
public:
|
||||||
|
BenchmarkT() {}
|
||||||
|
virtual ~BenchmarkT() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool ShouldRun(std::vector<regex_t*>&, T arg);
|
||||||
|
void RunWithArg(T arg);
|
||||||
|
virtual void RunIterations(int, T) = 0;
|
||||||
|
virtual std::string GetNameStr(T) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BenchmarkWithoutArg : public BenchmarkT<void*> {
|
||||||
|
public:
|
||||||
|
BenchmarkWithoutArg() {}
|
||||||
|
virtual ~BenchmarkWithoutArg() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual size_t RunAllArgs(std::vector<regex_t*>& regs) override {
|
||||||
|
size_t benchmarks_run = 0;
|
||||||
|
if (BenchmarkT<void*>::ShouldRun(regs, nullptr)) {
|
||||||
|
PrintHeader();
|
||||||
|
RunWithArg(nullptr);
|
||||||
|
benchmarks_run++;
|
||||||
|
}
|
||||||
|
return benchmarks_run;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void RunIterations(int iters, void*) override {
|
||||||
|
Run(iters);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Run(int) = 0;
|
||||||
|
|
||||||
|
virtual size_t NameColumnWidth() override {
|
||||||
|
return Name().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string GetNameStr(void *) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class BenchmarkWithArg : public BenchmarkT<T> {
|
||||||
|
public:
|
||||||
|
BenchmarkWithArg() {}
|
||||||
|
virtual ~BenchmarkWithArg() {}
|
||||||
|
|
||||||
|
BenchmarkWithArg* Arg(T arg) {
|
||||||
|
args_.push_back(arg);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual size_t NameColumnWidth() override {
|
||||||
|
size_t max = 0;
|
||||||
|
for (const auto& arg : args_) {
|
||||||
|
max = std::max(max, GetNameStr(arg).size());
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetNameStr(T arg) override;
|
||||||
|
|
||||||
|
virtual size_t RunAllArgs(std::vector<regex_t*>& regs) override {
|
||||||
|
size_t benchmarks_run = 0;
|
||||||
|
for (T& arg : args_) {
|
||||||
|
if (BenchmarkT<T>::ShouldRun(regs, arg)) {
|
||||||
|
Benchmark::PrintHeader();
|
||||||
|
BenchmarkT<T>::RunWithArg(arg);
|
||||||
|
benchmarks_run++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return benchmarks_run;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void RunIterations(int iters, T arg) override {
|
||||||
|
Run(iters, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Run(int iters, T arg) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<T> args_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#define BENCHMARK_START(f, super_class) \
|
||||||
|
class f : public super_class { \
|
||||||
|
public: \
|
||||||
|
f() {} \
|
||||||
|
virtual ~f() {} \
|
||||||
|
virtual std::string Name() override { return #f; } \
|
||||||
|
|
||||||
|
#define BENCHMARK_NO_ARG(f) \
|
||||||
|
BENCHMARK_START(f, ::testing::BenchmarkWithoutArg) \
|
||||||
|
virtual void Run(int) override; \
|
||||||
|
}; \
|
||||||
|
static ::testing::Benchmark* __benchmark_##f = new f()
|
||||||
|
|
||||||
|
#define BENCHMARK_WITH_ARG(f, arg_type) \
|
||||||
|
BENCHMARK_START(f, ::testing::BenchmarkWithArg<arg_type>) \
|
||||||
|
virtual void Run(int, arg_type) override; \
|
||||||
|
}; \
|
||||||
|
static ::testing::BenchmarkWithArg<arg_type>* __benchmark_##f = (new f())
|
||||||
|
|
||||||
|
#endif // BENCHMARKS_BENCHMARK_H_
|
@ -1,266 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <benchmark.h>
|
|
||||||
|
|
||||||
#include <regex.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
static int64_t g_bytes_processed;
|
|
||||||
static int64_t g_benchmark_total_time_ns;
|
|
||||||
static int64_t g_benchmark_start_time_ns;
|
|
||||||
static int g_name_column_width = 20;
|
|
||||||
|
|
||||||
typedef std::vector<::testing::Benchmark*> BenchmarkList;
|
|
||||||
|
|
||||||
static BenchmarkList& Benchmarks() {
|
|
||||||
static BenchmarkList benchmarks;
|
|
||||||
return benchmarks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Similar to the code in art, but supporting both binary and decimal prefixes.
|
|
||||||
static std::string PrettyInt(uint64_t count, size_t base) {
|
|
||||||
if (base != 2 && base != 10) abort();
|
|
||||||
|
|
||||||
// The byte thresholds at which we display amounts. A count is displayed
|
|
||||||
// in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
|
|
||||||
static const uint64_t kUnitThresholds2[] = {
|
|
||||||
1024*1024*1024 /* Gi */, 2*1024*1024 /* Mi */, 3*1024 /* Ki */, 0,
|
|
||||||
};
|
|
||||||
static const uint64_t kUnitThresholds10[] = {
|
|
||||||
1000*1000*1000 /* G */, 2*1000*1000 /* M */, 3*1000 /* k */, 0,
|
|
||||||
};
|
|
||||||
static const uint64_t kAmountPerUnit2[] = { 1024*1024*1024, 1024*1024, 1024, 1 };
|
|
||||||
static const uint64_t kAmountPerUnit10[] = { 1000*1000*1000, 1000*1000, 1000, 1 };
|
|
||||||
static const char* const kUnitStrings2[] = { "Gi", "Mi", "Ki", "" };
|
|
||||||
static const char* const kUnitStrings10[] = { "G", "M", "k", "" };
|
|
||||||
|
|
||||||
// Which set are we using?
|
|
||||||
const uint64_t* kUnitThresholds = ((base == 2) ? kUnitThresholds2 : kUnitThresholds10);
|
|
||||||
const uint64_t* kAmountPerUnit = ((base == 2) ? kAmountPerUnit2 : kAmountPerUnit10);
|
|
||||||
const char* const* kUnitStrings = ((base == 2) ? kUnitStrings2 : kUnitStrings10);
|
|
||||||
|
|
||||||
size_t i = 0;
|
|
||||||
for (; kUnitThresholds[i] != 0; ++i) {
|
|
||||||
if (count >= kUnitThresholds[i]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
char* s = NULL;
|
|
||||||
asprintf(&s, "%" PRId64 "%s", count / kAmountPerUnit[i], kUnitStrings[i]);
|
|
||||||
std::string result(s);
|
|
||||||
free(s);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Round(int n) {
|
|
||||||
int base = 1;
|
|
||||||
while (base*10 < n) {
|
|
||||||
base *= 10;
|
|
||||||
}
|
|
||||||
if (n < 2*base) {
|
|
||||||
return 2*base;
|
|
||||||
}
|
|
||||||
if (n < 5*base) {
|
|
||||||
return 5*base;
|
|
||||||
}
|
|
||||||
return 10*base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t NanoTime() {
|
|
||||||
struct timespec t;
|
|
||||||
t.tv_sec = t.tv_nsec = 0;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
|
||||||
return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace testing {
|
|
||||||
|
|
||||||
Benchmark* Benchmark::Arg(int arg) {
|
|
||||||
args_.push_back(arg);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Benchmark::Name() {
|
|
||||||
return name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Benchmark::ShouldRun(int argc, char* argv[]) {
|
|
||||||
if (argc == 1) {
|
|
||||||
return true; // With no arguments, we run all benchmarks.
|
|
||||||
}
|
|
||||||
// Otherwise, we interpret each argument as a regular expression and
|
|
||||||
// see if any of our benchmarks match.
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
regex_t re;
|
|
||||||
if (regcomp(&re, argv[i], 0) != 0) {
|
|
||||||
fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
int match = regexec(&re, name_, 0, NULL, 0);
|
|
||||||
regfree(&re);
|
|
||||||
if (match != REG_NOMATCH) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
|
|
||||||
name_ = name;
|
|
||||||
fn_ = fn;
|
|
||||||
fn_range_ = fn_range;
|
|
||||||
|
|
||||||
if (fn_ == NULL && fn_range_ == NULL) {
|
|
||||||
fprintf(stderr, "%s: missing function\n", name_);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Benchmarks().push_back(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Benchmark::Run() {
|
|
||||||
if (fn_ != NULL) {
|
|
||||||
RunWithArg(0);
|
|
||||||
} else {
|
|
||||||
if (args_.empty()) {
|
|
||||||
fprintf(stderr, "%s: no args!\n", name_);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < args_.size(); ++i) {
|
|
||||||
RunWithArg(args_[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
|
|
||||||
g_bytes_processed = 0;
|
|
||||||
g_benchmark_total_time_ns = 0;
|
|
||||||
g_benchmark_start_time_ns = NanoTime();
|
|
||||||
if (fn_ != NULL) {
|
|
||||||
fn_(iterations);
|
|
||||||
} else {
|
|
||||||
fn_range_(iterations, arg);
|
|
||||||
}
|
|
||||||
if (g_benchmark_start_time_ns != 0) {
|
|
||||||
g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Benchmark::RunWithArg(int arg) {
|
|
||||||
// Run once in case it's expensive.
|
|
||||||
int iterations = 1;
|
|
||||||
int64_t realStartTime = NanoTime();
|
|
||||||
RunRepeatedlyWithArg(iterations, arg);
|
|
||||||
int64_t realTotalTime = NanoTime() - realStartTime;
|
|
||||||
while (realTotalTime < 1e9 && iterations < 1e8) {
|
|
||||||
int last = iterations;
|
|
||||||
if (realTotalTime/iterations == 0) {
|
|
||||||
iterations = 1e9;
|
|
||||||
} else {
|
|
||||||
iterations = 1e9 / (realTotalTime/iterations);
|
|
||||||
}
|
|
||||||
iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
|
|
||||||
iterations = Round(iterations);
|
|
||||||
realStartTime = NanoTime();
|
|
||||||
RunRepeatedlyWithArg(iterations, arg);
|
|
||||||
realTotalTime = NanoTime() - realStartTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
char throughput[100];
|
|
||||||
throughput[0] = '\0';
|
|
||||||
|
|
||||||
if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) {
|
|
||||||
double gib_processed = static_cast<double>(g_bytes_processed)/1e9;
|
|
||||||
double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9;
|
|
||||||
snprintf(throughput, sizeof(throughput), " %8.3f GiB/s", gib_processed/seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
char full_name[100];
|
|
||||||
if (fn_range_ != NULL) {
|
|
||||||
snprintf(full_name, sizeof(full_name), "%s/%s", name_, PrettyInt(arg, 2).c_str());
|
|
||||||
} else {
|
|
||||||
snprintf(full_name, sizeof(full_name), "%s", name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%-*s %10s %10" PRId64 "%s\n",
|
|
||||||
g_name_column_width, full_name,
|
|
||||||
PrettyInt(iterations, 10).c_str(),
|
|
||||||
g_benchmark_total_time_ns/iterations,
|
|
||||||
throughput);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace testing
|
|
||||||
|
|
||||||
void SetBenchmarkBytesProcessed(int64_t x) {
|
|
||||||
g_bytes_processed = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StopBenchmarkTiming() {
|
|
||||||
if (g_benchmark_start_time_ns != 0) {
|
|
||||||
g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
|
|
||||||
}
|
|
||||||
g_benchmark_start_time_ns = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartBenchmarkTiming() {
|
|
||||||
if (g_benchmark_start_time_ns == 0) {
|
|
||||||
g_benchmark_start_time_ns = NanoTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (Benchmarks().empty()) {
|
|
||||||
fprintf(stderr, "No benchmarks registered!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& b : Benchmarks()) {
|
|
||||||
int name_width = static_cast<int>(strlen(b->Name()));
|
|
||||||
g_name_column_width = std::max(g_name_column_width, name_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool need_header = true;
|
|
||||||
for (auto& b : Benchmarks()) {
|
|
||||||
if (b->ShouldRun(argc, argv)) {
|
|
||||||
if (need_header) {
|
|
||||||
printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
|
|
||||||
fflush(stdout);
|
|
||||||
need_header = false;
|
|
||||||
}
|
|
||||||
b->Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (need_header) {
|
|
||||||
fprintf(stderr, "No matching benchmarks!\n");
|
|
||||||
fprintf(stderr, "Available benchmarks:\n");
|
|
||||||
for (auto& b : Benchmarks()) {
|
|
||||||
fprintf(stderr, " %s\n", b->Name());
|
|
||||||
}
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BENCHMARKS_BENCHMARK_H_
|
|
||||||
#define BENCHMARKS_BENCHMARK_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace testing {
|
|
||||||
|
|
||||||
class Benchmark {
|
|
||||||
public:
|
|
||||||
Benchmark(const char* name, void (*fn)(int)) {
|
|
||||||
Register(name, fn, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
Benchmark(const char* name, void (*fn_range)(int, int)) {
|
|
||||||
Register(name, NULL, fn_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
Benchmark* Arg(int x);
|
|
||||||
|
|
||||||
const char* Name();
|
|
||||||
|
|
||||||
bool ShouldRun(int argc, char* argv[]);
|
|
||||||
void Run();
|
|
||||||
|
|
||||||
private:
|
|
||||||
const char* name_;
|
|
||||||
|
|
||||||
void (*fn_)(int);
|
|
||||||
void (*fn_range_)(int, int);
|
|
||||||
|
|
||||||
std::vector<int> args_;
|
|
||||||
|
|
||||||
void Register(const char* name, void (*fn)(int), void (*fn_range)(int, int));
|
|
||||||
void RunRepeatedlyWithArg(int iterations, int arg);
|
|
||||||
void RunWithArg(int arg);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace testing
|
|
||||||
|
|
||||||
void SetBenchmarkBytesProcessed(int64_t);
|
|
||||||
void StopBenchmarkTiming();
|
|
||||||
void StartBenchmarkTiming();
|
|
||||||
|
|
||||||
#define BENCHMARK(f) \
|
|
||||||
static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \
|
|
||||||
(new ::testing::Benchmark(#f, f))
|
|
||||||
|
|
||||||
#endif
|
|
61
benchmarks/main.cpp
Normal file
61
benchmarks/main.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (::testing::Benchmark::List().empty()) {
|
||||||
|
fprintf(stderr, "No benchmarks registered!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<regex_t*> regs;
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
regex_t* re = new regex_t;
|
||||||
|
int errcode = regcomp(re, argv[i], 0);
|
||||||
|
if (errcode != 0) {
|
||||||
|
size_t errbuf_size = regerror(errcode, re, NULL, 0);
|
||||||
|
if (errbuf_size > 0) {
|
||||||
|
char* errbuf = new char[errbuf_size];
|
||||||
|
regerror(errcode, re, errbuf, errbuf_size);
|
||||||
|
fprintf(stderr, "Couldn't compile \"%s\" as a regular expression: %s\n",
|
||||||
|
argv[i], errbuf);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown compile error for \"%s\" as a regular expression!\n", argv[i]);
|
||||||
|
}
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
regs.push_back(re);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::testing::Benchmark::RunAll(regs) == 0) {
|
||||||
|
fprintf(stderr, "No matching benchmarks!\n");
|
||||||
|
fprintf(stderr, "Available benchmarks:\n");
|
||||||
|
for (const auto& benchmark : ::testing::Benchmark::List()) {
|
||||||
|
fprintf(stderr, " %s\n", benchmark->Name().c_str());
|
||||||
|
}
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -14,16 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
#include <fenv.h>
|
#include <fenv.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
|
#define AT_COMMON_VALS \
|
||||||
|
Arg(1234.0)->Arg(nan(""))->Arg(HUGE_VAL)->Arg(0.0)
|
||||||
|
|
||||||
// Avoid optimization.
|
// Avoid optimization.
|
||||||
volatile double d;
|
volatile double d;
|
||||||
volatile double v;
|
volatile double v;
|
||||||
|
|
||||||
static void BM_math_sqrt(int iters) {
|
BENCHMARK_NO_ARG(BM_math_sqrt);
|
||||||
|
void BM_math_sqrt::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
@ -34,9 +38,9 @@ static void BM_math_sqrt(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_sqrt);
|
|
||||||
|
|
||||||
static void BM_math_log10(int iters) {
|
BENCHMARK_NO_ARG(BM_math_log10);
|
||||||
|
void BM_math_log10::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
@ -47,9 +51,9 @@ static void BM_math_log10(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_log10);
|
|
||||||
|
|
||||||
static void BM_math_logb(int iters) {
|
BENCHMARK_NO_ARG(BM_math_logb);
|
||||||
|
void BM_math_logb::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
@ -60,61 +64,22 @@ static void BM_math_logb(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_logb);
|
|
||||||
|
|
||||||
static void BM_math_isinf_NORMAL(int iters) {
|
BENCHMARK_WITH_ARG(BM_math_isinf, double)->AT_COMMON_VALS;
|
||||||
|
void BM_math_isinf::Run(int iters, double value) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
v = 1234.0; // FP_NORMAL
|
v = value;
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
d += (isinf)(v);
|
d += (isinf)(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_isinf_NORMAL);
|
|
||||||
|
|
||||||
static void BM_math_isinf_NAN(int iters) {
|
BENCHMARK_NO_ARG(BM_math_sin_fast);
|
||||||
StartBenchmarkTiming();
|
void BM_math_sin_fast::Run(int iters) {
|
||||||
|
|
||||||
d = 0.0;
|
|
||||||
v = nan(""); // FP_NAN
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
|
||||||
d += (isinf)(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_math_isinf_NAN);
|
|
||||||
|
|
||||||
static void BM_math_isinf_INFINITE(int iters) {
|
|
||||||
StartBenchmarkTiming();
|
|
||||||
|
|
||||||
d = 0.0;
|
|
||||||
v = HUGE_VAL; // FP_INFINITE
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
|
||||||
d += (isinf)(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_math_isinf_INFINITE);
|
|
||||||
|
|
||||||
static void BM_math_isinf_ZERO(int iters) {
|
|
||||||
StartBenchmarkTiming();
|
|
||||||
|
|
||||||
d = 0.0;
|
|
||||||
v = 0.0; // FP_ZERO
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
|
||||||
d += (isinf)(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_math_isinf_ZERO);
|
|
||||||
|
|
||||||
static void BM_math_sin_fast(int iters) {
|
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 1.0;
|
d = 1.0;
|
||||||
@ -124,9 +89,9 @@ static void BM_math_sin_fast(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_sin_fast);
|
|
||||||
|
|
||||||
static void BM_math_sin_feupdateenv(int iters) {
|
BENCHMARK_NO_ARG(BM_math_sin_feupdateenv);
|
||||||
|
void BM_math_sin_feupdateenv::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 1.0;
|
d = 1.0;
|
||||||
@ -140,9 +105,9 @@ static void BM_math_sin_feupdateenv(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_sin_feupdateenv);
|
|
||||||
|
|
||||||
static void BM_math_sin_fesetenv(int iters) {
|
BENCHMARK_NO_ARG(BM_math_sin_fesetenv);
|
||||||
|
void BM_math_sin_fesetenv::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 1.0;
|
d = 1.0;
|
||||||
@ -156,56 +121,16 @@ static void BM_math_sin_fesetenv(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_sin_fesetenv);
|
|
||||||
|
|
||||||
static void BM_math_fpclassify_NORMAL(int iters) {
|
BENCHMARK_WITH_ARG(BM_math_fpclassify, double)->AT_COMMON_VALS;
|
||||||
|
void BM_math_fpclassify::Run(int iters, double value) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
v = 1234.0; // FP_NORMAL
|
v = value;
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
d += fpclassify(v);
|
d += fpclassify(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_math_fpclassify_NORMAL);
|
|
||||||
|
|
||||||
static void BM_math_fpclassify_NAN(int iters) {
|
|
||||||
StartBenchmarkTiming();
|
|
||||||
|
|
||||||
d = 0.0;
|
|
||||||
v = nan(""); // FP_NAN
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
|
||||||
d += fpclassify(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_math_fpclassify_NAN);
|
|
||||||
|
|
||||||
static void BM_math_fpclassify_INFINITE(int iters) {
|
|
||||||
StartBenchmarkTiming();
|
|
||||||
|
|
||||||
d = 0.0;
|
|
||||||
v = HUGE_VAL; // FP_INFINITE
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
|
||||||
d += fpclassify(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_math_fpclassify_INFINITE);
|
|
||||||
|
|
||||||
static void BM_math_fpclassify_ZERO(int iters) {
|
|
||||||
StartBenchmarkTiming();
|
|
||||||
|
|
||||||
d = 0.0;
|
|
||||||
v = 0.0; // FP_ZERO
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
|
||||||
d += fpclassify(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_math_fpclassify_ZERO);
|
|
||||||
|
@ -14,19 +14,21 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#if defined(__BIONIC__)
|
||||||
|
|
||||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||||
#include <sys/_system_properties.h>
|
#include <sys/_system_properties.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <benchmark/Benchmark.h>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
extern void *__system_property_area__;
|
extern void* __system_property_area__;
|
||||||
|
|
||||||
// Do not exceed 512, that is about the largest number of properties
|
// Do not exceed 512, that is about the largest number of properties
|
||||||
// that can be created with the current property area size.
|
// that can be created with the current property area size.
|
||||||
@ -34,200 +36,198 @@ extern void *__system_property_area__;
|
|||||||
Arg(1)->Arg(4)->Arg(16)->Arg(64)->Arg(128)->Arg(256)->Arg(512)
|
Arg(1)->Arg(4)->Arg(16)->Arg(64)->Arg(128)->Arg(256)->Arg(512)
|
||||||
|
|
||||||
struct LocalPropertyTestState {
|
struct LocalPropertyTestState {
|
||||||
LocalPropertyTestState(int nprops) : nprops(nprops), valid(false) {
|
LocalPropertyTestState(int nprops) : nprops(nprops), valid(false) {
|
||||||
static const char prop_name_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
|
static const char prop_name_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
|
||||||
|
|
||||||
const char* android_data = getenv("ANDROID_DATA");
|
const char* android_data = getenv("ANDROID_DATA");
|
||||||
if (android_data == NULL) {
|
if (android_data == NULL) {
|
||||||
printf("ANDROID_DATA environment variable not set\n");
|
printf("ANDROID_DATA environment variable not set\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char dir_template[PATH_MAX];
|
char dir_template[PATH_MAX];
|
||||||
snprintf(dir_template, sizeof(dir_template), "%s/local/tmp/prop-XXXXXX", android_data);
|
snprintf(dir_template, sizeof(dir_template), "%s/local/tmp/prop-XXXXXX", android_data);
|
||||||
char *dirname = mkdtemp(dir_template);
|
char* dirname = mkdtemp(dir_template);
|
||||||
if (!dirname) {
|
if (!dirname) {
|
||||||
printf("making temp file for test state failed (is %s/local/tmp writable?): %s\n",
|
printf("making temp file for test state failed (is %s/local/tmp writable?): %s\n",
|
||||||
android_data, strerror(errno));
|
android_data, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
old_pa = __system_property_area__;
|
|
||||||
__system_property_area__ = NULL;
|
|
||||||
|
|
||||||
pa_dirname = dirname;
|
|
||||||
pa_filename = pa_dirname + "/__properties__";
|
|
||||||
|
|
||||||
__system_property_set_filename(pa_filename.c_str());
|
|
||||||
__system_property_area_init();
|
|
||||||
|
|
||||||
names = new char* [nprops];
|
|
||||||
name_lens = new int[nprops];
|
|
||||||
values = new char* [nprops];
|
|
||||||
value_lens = new int[nprops];
|
|
||||||
|
|
||||||
srandom(nprops);
|
|
||||||
|
|
||||||
for (int i = 0; i < nprops; i++) {
|
|
||||||
// Make sure the name has at least 10 characters to make
|
|
||||||
// it very unlikely to generate the same random name.
|
|
||||||
name_lens[i] = (random() % (PROP_NAME_MAX - 10)) + 10;
|
|
||||||
names[i] = new char[PROP_NAME_MAX + 1];
|
|
||||||
size_t prop_name_len = sizeof(prop_name_chars) - 1;
|
|
||||||
for (int j = 0; j < name_lens[i]; j++) {
|
|
||||||
if (j == 0 || names[i][j-1] == '.' || j == name_lens[i] - 1) {
|
|
||||||
// Certain values are not allowed:
|
|
||||||
// - Don't start name with '.'
|
|
||||||
// - Don't allow '.' to appear twice in a row
|
|
||||||
// - Don't allow the name to end with '.'
|
|
||||||
// This assumes that '.' is the last character in the
|
|
||||||
// array so that decrementing the length by one removes
|
|
||||||
// the value from the possible values.
|
|
||||||
prop_name_len--;
|
|
||||||
}
|
|
||||||
names[i][j] = prop_name_chars[random() % prop_name_len];
|
|
||||||
}
|
|
||||||
names[i][name_lens[i]] = 0;
|
|
||||||
|
|
||||||
// Make sure the value contains at least 1 character.
|
|
||||||
value_lens[i] = (random() % (PROP_VALUE_MAX - 1)) + 1;
|
|
||||||
values[i] = new char[PROP_VALUE_MAX];
|
|
||||||
for (int j = 0; j < value_lens[i]; j++) {
|
|
||||||
values[i][j] = prop_name_chars[random() % (sizeof(prop_name_chars) - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__system_property_add(names[i], name_lens[i], values[i], value_lens[i]) < 0) {
|
|
||||||
printf("Failed to add a property, terminating...\n");
|
|
||||||
printf("%s = %.*s\n", names[i], value_lens[i], values[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
valid = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~LocalPropertyTestState() {
|
old_pa = __system_property_area__;
|
||||||
if (!valid)
|
__system_property_area__ = NULL;
|
||||||
return;
|
|
||||||
|
|
||||||
__system_property_area__ = old_pa;
|
pa_dirname = dirname;
|
||||||
|
pa_filename = pa_dirname + "/__properties__";
|
||||||
|
|
||||||
__system_property_set_filename(PROP_FILENAME);
|
__system_property_set_filename(pa_filename.c_str());
|
||||||
unlink(pa_filename.c_str());
|
__system_property_area_init();
|
||||||
rmdir(pa_dirname.c_str());
|
|
||||||
|
|
||||||
for (int i = 0; i < nprops; i++) {
|
names = new char* [nprops];
|
||||||
delete names[i];
|
name_lens = new int[nprops];
|
||||||
delete values[i];
|
values = new char* [nprops];
|
||||||
|
value_lens = new int[nprops];
|
||||||
|
|
||||||
|
srandom(nprops);
|
||||||
|
|
||||||
|
for (int i = 0; i < nprops; i++) {
|
||||||
|
// Make sure the name has at least 10 characters to make
|
||||||
|
// it very unlikely to generate the same random name.
|
||||||
|
name_lens[i] = (random() % (PROP_NAME_MAX - 10)) + 10;
|
||||||
|
names[i] = new char[PROP_NAME_MAX + 1];
|
||||||
|
size_t prop_name_len = sizeof(prop_name_chars) - 1;
|
||||||
|
for (int j = 0; j < name_lens[i]; j++) {
|
||||||
|
if (j == 0 || names[i][j-1] == '.' || j == name_lens[i] - 1) {
|
||||||
|
// Certain values are not allowed:
|
||||||
|
// - Don't start name with '.'
|
||||||
|
// - Don't allow '.' to appear twice in a row
|
||||||
|
// - Don't allow the name to end with '.'
|
||||||
|
// This assumes that '.' is the last character in the
|
||||||
|
// array so that decrementing the length by one removes
|
||||||
|
// the value from the possible values.
|
||||||
|
prop_name_len--;
|
||||||
}
|
}
|
||||||
delete[] names;
|
names[i][j] = prop_name_chars[random() % prop_name_len];
|
||||||
delete[] name_lens;
|
}
|
||||||
delete[] values;
|
names[i][name_lens[i]] = 0;
|
||||||
delete[] value_lens;
|
|
||||||
|
// Make sure the value contains at least 1 character.
|
||||||
|
value_lens[i] = (random() % (PROP_VALUE_MAX - 1)) + 1;
|
||||||
|
values[i] = new char[PROP_VALUE_MAX];
|
||||||
|
for (int j = 0; j < value_lens[i]; j++) {
|
||||||
|
values[i][j] = prop_name_chars[random() % (sizeof(prop_name_chars) - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__system_property_add(names[i], name_lens[i], values[i], value_lens[i]) < 0) {
|
||||||
|
printf("Failed to add a property, terminating...\n");
|
||||||
|
printf("%s = %.*s\n", names[i], value_lens[i], values[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~LocalPropertyTestState() {
|
||||||
|
if (!valid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
__system_property_area__ = old_pa;
|
||||||
|
|
||||||
|
__system_property_set_filename(PROP_FILENAME);
|
||||||
|
unlink(pa_filename.c_str());
|
||||||
|
rmdir(pa_dirname.c_str());
|
||||||
|
|
||||||
|
for (int i = 0; i < nprops; i++) {
|
||||||
|
delete names[i];
|
||||||
|
delete values[i];
|
||||||
|
}
|
||||||
|
delete[] names;
|
||||||
|
delete[] name_lens;
|
||||||
|
delete[] values;
|
||||||
|
delete[] value_lens;
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
const int nprops;
|
const int nprops;
|
||||||
char **names;
|
char** names;
|
||||||
int *name_lens;
|
int* name_lens;
|
||||||
char **values;
|
char** values;
|
||||||
int *value_lens;
|
int* value_lens;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string pa_dirname;
|
std::string pa_dirname;
|
||||||
std::string pa_filename;
|
std::string pa_filename;
|
||||||
void *old_pa;
|
void* old_pa;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void BM_property_get(int iters, int nprops)
|
BENCHMARK_WITH_ARG(BM_property_get, int)->TEST_NUM_PROPS;
|
||||||
{
|
void BM_property_get::Run(int iters, int nprops) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
LocalPropertyTestState pa(nprops);
|
LocalPropertyTestState pa(nprops);
|
||||||
char value[PROP_VALUE_MAX];
|
char value[PROP_VALUE_MAX];
|
||||||
|
|
||||||
if (!pa.valid)
|
if (!pa.valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
srandom(iters * nprops);
|
srandom(iters * nprops);
|
||||||
|
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
__system_property_get(pa.names[random() % nprops], value);
|
__system_property_get(pa.names[random() % nprops], value);
|
||||||
}
|
}
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_property_get)->TEST_NUM_PROPS;
|
|
||||||
|
|
||||||
static void BM_property_find(int iters, int nprops)
|
BENCHMARK_WITH_ARG(BM_property_find, int)->TEST_NUM_PROPS;
|
||||||
{
|
void BM_property_find::Run(int iters, int nprops) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
LocalPropertyTestState pa(nprops);
|
LocalPropertyTestState pa(nprops);
|
||||||
|
|
||||||
if (!pa.valid)
|
if (!pa.valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
srandom(iters * nprops);
|
srandom(iters * nprops);
|
||||||
|
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
__system_property_find(pa.names[random() % nprops]);
|
__system_property_find(pa.names[random() % nprops]);
|
||||||
}
|
}
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_property_find)->TEST_NUM_PROPS;
|
|
||||||
|
|
||||||
static void BM_property_read(int iters, int nprops)
|
BENCHMARK_WITH_ARG(BM_property_read, int)->TEST_NUM_PROPS;
|
||||||
{
|
void BM_property_read::Run(int iters, int nprops) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
LocalPropertyTestState pa(nprops);
|
LocalPropertyTestState pa(nprops);
|
||||||
|
|
||||||
if (!pa.valid)
|
if (!pa.valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
srandom(iters * nprops);
|
srandom(iters * nprops);
|
||||||
const prop_info** pinfo = new const prop_info*[iters];
|
const prop_info** pinfo = new const prop_info*[iters];
|
||||||
char propvalue[PROP_VALUE_MAX];
|
char propvalue[PROP_VALUE_MAX];
|
||||||
|
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
pinfo[i] = __system_property_find(pa.names[random() % nprops]);
|
pinfo[i] = __system_property_find(pa.names[random() % nprops]);
|
||||||
}
|
}
|
||||||
|
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
__system_property_read(pinfo[i], 0, propvalue);
|
__system_property_read(pinfo[i], 0, propvalue);
|
||||||
}
|
}
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
delete[] pinfo;
|
delete[] pinfo;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_property_read)->TEST_NUM_PROPS;
|
|
||||||
|
|
||||||
static void BM_property_serial(int iters, int nprops)
|
BENCHMARK_WITH_ARG(BM_property_serial, int)->TEST_NUM_PROPS;
|
||||||
{
|
void BM_property_serial::Run(int iters, int nprops) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
LocalPropertyTestState pa(nprops);
|
LocalPropertyTestState pa(nprops);
|
||||||
|
|
||||||
if (!pa.valid)
|
if (!pa.valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
srandom(iters * nprops);
|
srandom(iters * nprops);
|
||||||
const prop_info** pinfo = new const prop_info*[iters];
|
const prop_info** pinfo = new const prop_info*[iters];
|
||||||
|
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
pinfo[i] = __system_property_find(pa.names[random() % nprops]);
|
pinfo[i] = __system_property_find(pa.names[random() % nprops]);
|
||||||
}
|
}
|
||||||
|
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
__system_property_serial(pinfo[i]);
|
__system_property_serial(pinfo[i]);
|
||||||
}
|
}
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
delete[] pinfo;
|
delete[] pinfo;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_property_serial)->TEST_NUM_PROPS;
|
|
||||||
|
#endif // __BIONIC__
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
// Stop GCC optimizing out our pure function.
|
// Stop GCC optimizing out our pure function.
|
||||||
/* Must not be static! */ pthread_t (*pthread_self_fp)() = pthread_self;
|
/* Must not be static! */ pthread_t (*pthread_self_fp)() = pthread_self;
|
||||||
|
|
||||||
static void BM_pthread_self(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_self);
|
||||||
|
void BM_pthread_self::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
@ -30,9 +31,9 @@ static void BM_pthread_self(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_self);
|
|
||||||
|
|
||||||
static void BM_pthread_getspecific(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_getspecific);
|
||||||
|
void BM_pthread_getspecific::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_key_t key;
|
pthread_key_t key;
|
||||||
pthread_key_create(&key, NULL);
|
pthread_key_create(&key, NULL);
|
||||||
@ -45,9 +46,9 @@ static void BM_pthread_getspecific(int iters) {
|
|||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_key_delete(key);
|
pthread_key_delete(key);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_getspecific);
|
|
||||||
|
|
||||||
static void BM_pthread_setspecific(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_setspecific);
|
||||||
|
void BM_pthread_setspecific::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_key_t key;
|
pthread_key_t key;
|
||||||
pthread_key_create(&key, NULL);
|
pthread_key_create(&key, NULL);
|
||||||
@ -60,12 +61,12 @@ static void BM_pthread_setspecific(int iters) {
|
|||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_key_delete(key);
|
pthread_key_delete(key);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_setspecific);
|
|
||||||
|
|
||||||
static void DummyPthreadOnceInitFunction() {
|
static void DummyPthreadOnceInitFunction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_pthread_once(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_once);
|
||||||
|
void BM_pthread_once::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_once_t once = PTHREAD_ONCE_INIT;
|
pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||||
pthread_once(&once, DummyPthreadOnceInitFunction);
|
pthread_once(&once, DummyPthreadOnceInitFunction);
|
||||||
@ -77,9 +78,9 @@ static void BM_pthread_once(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_once);
|
|
||||||
|
|
||||||
static void BM_pthread_mutex_lock(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_mutex_lock);
|
||||||
|
void BM_pthread_mutex_lock::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
@ -91,9 +92,9 @@ static void BM_pthread_mutex_lock(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_mutex_lock);
|
|
||||||
|
|
||||||
static void BM_pthread_mutex_lock_ERRORCHECK(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_mutex_lock_ERRORCHECK);
|
||||||
|
void BM_pthread_mutex_lock_ERRORCHECK::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
@ -105,9 +106,9 @@ static void BM_pthread_mutex_lock_ERRORCHECK(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_mutex_lock_ERRORCHECK);
|
|
||||||
|
|
||||||
static void BM_pthread_mutex_lock_RECURSIVE(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_mutex_lock_RECURSIVE);
|
||||||
|
void BM_pthread_mutex_lock_RECURSIVE::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
@ -119,9 +120,9 @@ static void BM_pthread_mutex_lock_RECURSIVE(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_mutex_lock_RECURSIVE);
|
|
||||||
|
|
||||||
static void BM_pthread_rw_lock_read(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_rw_lock_read);
|
||||||
|
void BM_pthread_rw_lock_read::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_rwlock_t lock;
|
pthread_rwlock_t lock;
|
||||||
pthread_rwlock_init(&lock, NULL);
|
pthread_rwlock_init(&lock, NULL);
|
||||||
@ -135,9 +136,9 @@ static void BM_pthread_rw_lock_read(int iters) {
|
|||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_rwlock_destroy(&lock);
|
pthread_rwlock_destroy(&lock);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_rw_lock_read);
|
|
||||||
|
|
||||||
static void BM_pthread_rw_lock_write(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_rw_lock_write);
|
||||||
|
void BM_pthread_rw_lock_write::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_rwlock_t lock;
|
pthread_rwlock_t lock;
|
||||||
pthread_rwlock_init(&lock, NULL);
|
pthread_rwlock_init(&lock, NULL);
|
||||||
@ -151,13 +152,13 @@ static void BM_pthread_rw_lock_write(int iters) {
|
|||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_rwlock_destroy(&lock);
|
pthread_rwlock_destroy(&lock);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_rw_lock_write);
|
|
||||||
|
|
||||||
static void* IdleThread(void*) {
|
static void* IdleThread(void*) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_pthread_create(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_create);
|
||||||
|
void BM_pthread_create::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
||||||
@ -168,43 +169,45 @@ static void BM_pthread_create(int iters) {
|
|||||||
pthread_join(thread, NULL);
|
pthread_join(thread, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_create);
|
|
||||||
|
|
||||||
static void* RunThread(void*) {
|
static void* RunThread(void* arg) {
|
||||||
StopBenchmarkTiming();
|
::testing::Benchmark* benchmark = reinterpret_cast<::testing::Benchmark*>(arg);
|
||||||
|
benchmark->StopBenchmarkTiming();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_pthread_create_and_run(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_create_and_run);
|
||||||
|
void BM_pthread_create_and_run::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
pthread_create(&thread, NULL, RunThread, NULL);
|
pthread_create(&thread, NULL, RunThread, this);
|
||||||
pthread_join(thread, NULL);
|
pthread_join(thread, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_create_and_run);
|
|
||||||
|
|
||||||
static void* ExitThread(void*) {
|
static void* ExitThread(void* arg) {
|
||||||
StartBenchmarkTiming();
|
::testing::Benchmark* benchmark = reinterpret_cast<::testing::Benchmark*>(arg);
|
||||||
|
benchmark->StartBenchmarkTiming();
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_pthread_exit_and_join(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_exit_and_join);
|
||||||
|
void BM_pthread_exit_and_join::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
pthread_create(&thread, NULL, ExitThread, NULL);
|
pthread_create(&thread, NULL, ExitThread, this);
|
||||||
pthread_join(thread, NULL);
|
pthread_join(thread, NULL);
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_exit_and_join);
|
|
||||||
|
|
||||||
static void BM_pthread_key_create(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_key_create);
|
||||||
|
void BM_pthread_key_create::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_key_t key;
|
pthread_key_t key;
|
||||||
|
|
||||||
@ -215,9 +218,9 @@ static void BM_pthread_key_create(int iters) {
|
|||||||
pthread_key_delete(key);
|
pthread_key_delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_key_create);
|
|
||||||
|
|
||||||
static void BM_pthread_key_delete(int iters) {
|
BENCHMARK_NO_ARG(BM_pthread_key_delete);
|
||||||
|
void BM_pthread_key_delete::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
pthread_key_t key;
|
pthread_key_t key;
|
||||||
|
|
||||||
@ -228,4 +231,3 @@ static void BM_pthread_key_delete(int iters) {
|
|||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_pthread_key_delete);
|
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static void BM_semaphore_sem_getvalue(int iters) {
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
|
BENCHMARK_NO_ARG(BM_semaphore_sem_getvalue);
|
||||||
|
void BM_semaphore_sem_getvalue::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
sem_t semaphore;
|
sem_t semaphore;
|
||||||
sem_init(&semaphore, 1, 1);
|
sem_init(&semaphore, 1, 1);
|
||||||
@ -34,9 +35,9 @@ static void BM_semaphore_sem_getvalue(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_semaphore_sem_getvalue);
|
|
||||||
|
|
||||||
static void BM_semaphore_sem_wait_sem_post(int iters) {
|
BENCHMARK_NO_ARG(BM_semaphore_sem_wait_sem_post);
|
||||||
|
void BM_semaphore_sem_wait_sem_post::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
sem_t semaphore;
|
sem_t semaphore;
|
||||||
sem_init(&semaphore, 1, 1);
|
sem_init(&semaphore, 1, 1);
|
||||||
@ -49,7 +50,6 @@ static void BM_semaphore_sem_wait_sem_post(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_semaphore_sem_wait_sem_post);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This test reports the overhead of the underlying futex wake syscall on
|
* This test reports the overhead of the underlying futex wake syscall on
|
||||||
@ -87,7 +87,8 @@ static void *BM_semaphore_sem_post_start_thread(void *obj) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_semaphore_sem_post(int iters) {
|
BENCHMARK_NO_ARG(BM_semaphore_sem_post);
|
||||||
|
void BM_semaphore_sem_post::Run(int iters) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
|
|
||||||
sem_t semaphore;
|
sem_t semaphore;
|
||||||
@ -100,9 +101,6 @@ static void BM_semaphore_sem_post(int iters) {
|
|||||||
pthread_attr_setschedparam(&attr, ¶m);
|
pthread_attr_setschedparam(&attr, ¶m);
|
||||||
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
#ifdef PTHREAD_SET_INHERIT_SCHED
|
|
||||||
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
|
||||||
#endif
|
|
||||||
pthread_t pthread;
|
pthread_t pthread;
|
||||||
pthread_create(&pthread, &attr, BM_semaphore_sem_post_start_thread, &semaphore);
|
pthread_create(&pthread, &attr, BM_semaphore_sem_post_start_thread, &semaphore);
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
@ -143,99 +141,3 @@ static void BM_semaphore_sem_post(int iters) {
|
|||||||
sched_yield();
|
sched_yield();
|
||||||
} while (!BM_semaphore_sem_post_running);
|
} while (!BM_semaphore_sem_post_running);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_semaphore_sem_post);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This test reports the overhead of sem_post to sem_wake. A circle of
|
|
||||||
* num_semaphore - 1 threads are run on a set of semaphores to measure the
|
|
||||||
* activity. One can calculate the sem_wake overhead alone by:
|
|
||||||
*
|
|
||||||
* BM_semaphore_sem_post_sem_wait - BM_semaphore_sem_post - BM_time_clock_gettime
|
|
||||||
*
|
|
||||||
* Differences will result if there are more threads than active processors,
|
|
||||||
* there will be delay induced when scheduling the processes. This cost is
|
|
||||||
* measured by trying different values of num_semaphore. The governor selected
|
|
||||||
* will have a major impact on the results for a large number of threads.
|
|
||||||
*
|
|
||||||
* To reduce the chances for threads racing ahead and not triggering the
|
|
||||||
* futex, for example the background threads finish their job before the
|
|
||||||
* sem_wait is hit in the main thread, the background threads will run at
|
|
||||||
* batch priority and the main thread at fifo priority. This should generally
|
|
||||||
* guarantee the main thread completes its task of priming itself with the
|
|
||||||
* sem_wait before the other threads can start. In practice without the
|
|
||||||
* sched mechanics here, this works on Android configured kernels, this is
|
|
||||||
* insurance for wacky(tm) sched configurations.
|
|
||||||
*/
|
|
||||||
static void *BM_semaphore_sem_post_sem_wait_start_thread(void *obj) {
|
|
||||||
sem_t *semaphore = reinterpret_cast<sem_t *>(obj);
|
|
||||||
|
|
||||||
while ((BM_semaphore_sem_post_running > 0) && !sem_wait(semaphore)) {
|
|
||||||
sem_post(semaphore + 1);
|
|
||||||
}
|
|
||||||
--BM_semaphore_sem_post_running;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void BM_semaphore_sem_post_sem_wait_num(int iters, int num_semaphore) {
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
|
|
||||||
sem_t semaphore[num_semaphore];
|
|
||||||
|
|
||||||
for (int i = 0; i < num_semaphore; ++i) {
|
|
||||||
sem_init(semaphore + i, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_attr_t attr;
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
BM_semaphore_sem_post_running = 1;
|
|
||||||
struct sched_param param = { 0, };
|
|
||||||
pthread_attr_setschedparam(&attr, ¶m);
|
|
||||||
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
|
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
||||||
#ifdef PTHREAD_SET_INHERIT_SCHED
|
|
||||||
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
|
||||||
#endif
|
|
||||||
for (int i = 0; i < (num_semaphore - 1); ++i) {
|
|
||||||
pthread_t pthread;
|
|
||||||
pthread_create(&pthread, &attr, BM_semaphore_sem_post_sem_wait_start_thread, semaphore + i);
|
|
||||||
}
|
|
||||||
pthread_attr_destroy(&attr);
|
|
||||||
sched_yield();
|
|
||||||
|
|
||||||
param.sched_priority = 1;
|
|
||||||
sched_setscheduler((pid_t)0, SCHED_FIFO, ¶m);
|
|
||||||
|
|
||||||
StartBenchmarkTiming();
|
|
||||||
|
|
||||||
for (int i = 0; i < iters; i += num_semaphore) {
|
|
||||||
sem_post(semaphore);
|
|
||||||
sem_wait(semaphore + num_semaphore - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
|
||||||
|
|
||||||
param.sched_priority = 0;
|
|
||||||
sched_setscheduler((pid_t)0, SCHED_OTHER, ¶m);
|
|
||||||
|
|
||||||
if (BM_semaphore_sem_post_running > 0) {
|
|
||||||
BM_semaphore_sem_post_running = 0;
|
|
||||||
}
|
|
||||||
for (int i = 0;
|
|
||||||
(i < (10 * num_semaphore)) && (BM_semaphore_sem_post_running > (1 - num_semaphore));
|
|
||||||
++i) {
|
|
||||||
for (int j = 0; j < (num_semaphore - 1); ++j) {
|
|
||||||
sem_post(semaphore + j);
|
|
||||||
}
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void BM_semaphore_sem_post_sem_wait_low(int iters) {
|
|
||||||
BM_semaphore_sem_post_sem_wait_num(iters, 2);
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_semaphore_sem_post_sem_wait_low);
|
|
||||||
|
|
||||||
static void BM_semaphore_sem_post_sem_wait_high(int iters) {
|
|
||||||
BM_semaphore_sem_post_sem_wait_num(iters, 100);
|
|
||||||
}
|
|
||||||
BENCHMARK(BM_semaphore_sem_post_sem_wait_high);
|
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdio_ext.h>
|
#include <stdio_ext.h>
|
||||||
|
|
||||||
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
#define KB 1024
|
#define KB 1024
|
||||||
#define MB 1024*KB
|
#define MB 1024*KB
|
||||||
|
|
||||||
@ -27,12 +27,12 @@
|
|||||||
Arg(1*KB)->Arg(4*KB)->Arg(8*KB)->Arg(16*KB)->Arg(64*KB)
|
Arg(1*KB)->Arg(4*KB)->Arg(8*KB)->Arg(16*KB)->Arg(64*KB)
|
||||||
|
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
static void ReadWriteTest(int iters, int chunk_size, Fn f, bool buffered) {
|
void ReadWriteTest(::testing::Benchmark* benchmark, int iters, int chunk_size, Fn f, bool buffered) {
|
||||||
StopBenchmarkTiming();
|
benchmark->StopBenchmarkTiming();
|
||||||
FILE* fp = fopen("/dev/zero", "rw");
|
FILE* fp = fopen("/dev/zero", "rw");
|
||||||
__fsetlocking(fp, FSETLOCKING_BYCALLER);
|
__fsetlocking(fp, FSETLOCKING_BYCALLER);
|
||||||
char* buf = new char[chunk_size];
|
char* buf = new char[chunk_size];
|
||||||
StartBenchmarkTiming();
|
benchmark->StartBenchmarkTiming();
|
||||||
|
|
||||||
if (!buffered) {
|
if (!buffered) {
|
||||||
setvbuf(fp, 0, _IONBF, 0);
|
setvbuf(fp, 0, _IONBF, 0);
|
||||||
@ -42,31 +42,31 @@ static void ReadWriteTest(int iters, int chunk_size, Fn f, bool buffered) {
|
|||||||
f(buf, chunk_size, 1, fp);
|
f(buf, chunk_size, 1, fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
benchmark->StopBenchmarkTiming();
|
||||||
SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(chunk_size));
|
benchmark->SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(chunk_size));
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_stdio_fread(int iters, int chunk_size) {
|
BENCHMARK_WITH_ARG(BM_stdio_fread, int)->AT_COMMON_SIZES;
|
||||||
ReadWriteTest(iters, chunk_size, fread, true);
|
void BM_stdio_fread::Run(int iters, int chunk_size) {
|
||||||
|
ReadWriteTest(this, iters, chunk_size, fread, true);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_stdio_fread)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_stdio_fwrite(int iters, int chunk_size) {
|
BENCHMARK_WITH_ARG(BM_stdio_fwrite, int)->AT_COMMON_SIZES;
|
||||||
ReadWriteTest(iters, chunk_size, fwrite, true);
|
void BM_stdio_fwrite::Run(int iters, int chunk_size) {
|
||||||
|
ReadWriteTest(this, iters, chunk_size, fwrite, true);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_stdio_fwrite)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_stdio_fread_unbuffered(int iters, int chunk_size) {
|
BENCHMARK_WITH_ARG(BM_stdio_fread_unbuffered, int)->AT_COMMON_SIZES;
|
||||||
ReadWriteTest(iters, chunk_size, fread, false);
|
void BM_stdio_fread_unbuffered::Run(int iters, int chunk_size) {
|
||||||
|
ReadWriteTest(this, iters, chunk_size, fread, false);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_stdio_fread_unbuffered)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_stdio_fwrite_unbuffered(int iters, int chunk_size) {
|
BENCHMARK_WITH_ARG(BM_stdio_fwrite_unbuffered, int)->AT_COMMON_SIZES;
|
||||||
ReadWriteTest(iters, chunk_size, fwrite, false);
|
void BM_stdio_fwrite_unbuffered::Run(int iters, int chunk_size) {
|
||||||
|
ReadWriteTest(this, iters, chunk_size, fwrite, false);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_stdio_fwrite_unbuffered)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void FopenFgetsFclose(int iters, bool no_locking) {
|
static void FopenFgetsFclose(int iters, bool no_locking) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
@ -78,12 +78,16 @@ static void FopenFgetsFclose(int iters, bool no_locking) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BM_stdio_fopen_fgets_fclose_locking(int iters) {
|
BENCHMARK_NO_ARG(BM_stdio_fopen_fgets_fclose_locking);
|
||||||
|
void BM_stdio_fopen_fgets_fclose_locking::Run(int iters) {
|
||||||
|
StartBenchmarkTiming();
|
||||||
FopenFgetsFclose(iters, false);
|
FopenFgetsFclose(iters, false);
|
||||||
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_stdio_fopen_fgets_fclose_locking);
|
|
||||||
|
|
||||||
static void BM_stdio_fopen_fgets_fclose_no_locking(int iters) {
|
BENCHMARK_NO_ARG(BM_stdio_fopen_fgets_fclose_no_locking);
|
||||||
|
void BM_stdio_fopen_fgets_fclose_no_locking::Run(int iters) {
|
||||||
|
StartBenchmarkTiming();
|
||||||
FopenFgetsFclose(iters, true);
|
FopenFgetsFclose(iters, true);
|
||||||
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_stdio_fopen_fgets_fclose_no_locking);
|
|
||||||
|
@ -14,10 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
#define KB 1024
|
#define KB 1024
|
||||||
#define MB 1024*KB
|
#define MB 1024*KB
|
||||||
|
|
||||||
@ -26,7 +27,8 @@
|
|||||||
|
|
||||||
// TODO: test unaligned operation too? (currently everything will be 8-byte aligned by malloc.)
|
// TODO: test unaligned operation too? (currently everything will be 8-byte aligned by malloc.)
|
||||||
|
|
||||||
static void BM_string_memcmp(int iters, int nbytes) {
|
BENCHMARK_WITH_ARG(BM_string_memcmp, int)->AT_COMMON_SIZES;
|
||||||
|
void BM_string_memcmp::Run(int iters, int nbytes) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
char* src = new char[nbytes]; char* dst = new char[nbytes];
|
char* src = new char[nbytes]; char* dst = new char[nbytes];
|
||||||
memset(src, 'x', nbytes);
|
memset(src, 'x', nbytes);
|
||||||
@ -39,13 +41,13 @@ static void BM_string_memcmp(int iters, int nbytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(nbytes));
|
SetBenchmarkBytesProcessed(uint64_t(iters) * uint64_t(nbytes));
|
||||||
delete[] src;
|
delete[] src;
|
||||||
delete[] dst;
|
delete[] dst;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_string_memcmp)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_string_memcpy(int iters, int nbytes) {
|
BENCHMARK_WITH_ARG(BM_string_memcpy, int)->AT_COMMON_SIZES;
|
||||||
|
void BM_string_memcpy::Run(int iters, int nbytes) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
char* src = new char[nbytes]; char* dst = new char[nbytes];
|
char* src = new char[nbytes]; char* dst = new char[nbytes];
|
||||||
memset(src, 'x', nbytes);
|
memset(src, 'x', nbytes);
|
||||||
@ -56,13 +58,13 @@ static void BM_string_memcpy(int iters, int nbytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(nbytes));
|
SetBenchmarkBytesProcessed(uint64_t(iters) * uint64_t(nbytes));
|
||||||
delete[] src;
|
delete[] src;
|
||||||
delete[] dst;
|
delete[] dst;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_string_memcpy)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_string_memmove(int iters, int nbytes) {
|
BENCHMARK_WITH_ARG(BM_string_memmove, int)->AT_COMMON_SIZES;
|
||||||
|
void BM_string_memmove::Run(int iters, int nbytes) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
char* buf = new char[nbytes + 64];
|
char* buf = new char[nbytes + 64];
|
||||||
memset(buf, 'x', nbytes + 64);
|
memset(buf, 'x', nbytes + 64);
|
||||||
@ -73,12 +75,12 @@ static void BM_string_memmove(int iters, int nbytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(nbytes));
|
SetBenchmarkBytesProcessed(uint64_t(iters) * uint64_t(nbytes));
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_string_memmove)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_string_memset(int iters, int nbytes) {
|
BENCHMARK_WITH_ARG(BM_string_memset, int)->AT_COMMON_SIZES;
|
||||||
|
void BM_string_memset::Run(int iters, int nbytes) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
char* dst = new char[nbytes];
|
char* dst = new char[nbytes];
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
@ -88,12 +90,12 @@ static void BM_string_memset(int iters, int nbytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(nbytes));
|
SetBenchmarkBytesProcessed(uint64_t(iters) * uint64_t(nbytes));
|
||||||
delete[] dst;
|
delete[] dst;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_string_memset)->AT_COMMON_SIZES;
|
|
||||||
|
|
||||||
static void BM_string_strlen(int iters, int nbytes) {
|
BENCHMARK_WITH_ARG(BM_string_strlen, int)->AT_COMMON_SIZES;
|
||||||
|
void BM_string_strlen::Run(int iters, int nbytes) {
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
char* s = new char[nbytes];
|
char* s = new char[nbytes];
|
||||||
memset(s, 'x', nbytes);
|
memset(s, 'x', nbytes);
|
||||||
@ -106,7 +108,6 @@ static void BM_string_strlen(int iters, int nbytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
SetBenchmarkBytesProcessed(int64_t(iters) * int64_t(nbytes));
|
SetBenchmarkBytesProcessed(uint64_t(iters) * uint64_t(nbytes));
|
||||||
delete[] s;
|
delete[] s;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_string_strlen)->AT_COMMON_SIZES;
|
|
||||||
|
@ -14,14 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
static void BM_time_clock_gettime(int iters) {
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
|
BENCHMARK_NO_ARG(BM_time_clock_gettime);
|
||||||
|
void BM_time_clock_gettime::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
timespec t;
|
timespec t;
|
||||||
@ -31,9 +31,9 @@ static void BM_time_clock_gettime(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_time_clock_gettime);
|
|
||||||
|
|
||||||
static void BM_time_clock_gettime_syscall(int iters) {
|
BENCHMARK_NO_ARG(BM_time_clock_gettime_syscall);
|
||||||
|
void BM_time_clock_gettime_syscall::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
timespec t;
|
timespec t;
|
||||||
@ -43,9 +43,9 @@ static void BM_time_clock_gettime_syscall(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_time_clock_gettime_syscall);
|
|
||||||
|
|
||||||
static void BM_time_gettimeofday(int iters) {
|
BENCHMARK_NO_ARG(BM_time_gettimeofday);
|
||||||
|
void BM_time_gettimeofday::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
timeval tv;
|
timeval tv;
|
||||||
@ -55,9 +55,9 @@ static void BM_time_gettimeofday(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_time_gettimeofday);
|
|
||||||
|
|
||||||
static void BM_time_gettimeofday_syscall(int iters) {
|
BENCHMARK_NO_ARG(BM_time_gettimeofday_syscall);
|
||||||
|
void BM_time_gettimeofday_syscall::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
timeval tv;
|
timeval tv;
|
||||||
@ -67,9 +67,9 @@ static void BM_time_gettimeofday_syscall(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_time_gettimeofday_syscall);
|
|
||||||
|
|
||||||
static void BM_time_time(int iters) {
|
BENCHMARK_NO_ARG(BM_time_time);
|
||||||
|
void BM_time_time::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
@ -78,4 +78,3 @@ static void BM_time_time(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_time_time);
|
|
||||||
|
@ -14,12 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static void BM_unistd_getpid(int iters) {
|
#include <benchmark/Benchmark.h>
|
||||||
|
|
||||||
|
BENCHMARK_NO_ARG(BM_unistd_getpid);
|
||||||
|
void BM_unistd_getpid::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
@ -28,9 +29,9 @@ static void BM_unistd_getpid(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_unistd_getpid);
|
|
||||||
|
|
||||||
static void BM_unistd_getpid_syscall(int iters) {
|
BENCHMARK_NO_ARG(BM_unistd_getpid_syscall);
|
||||||
|
void BM_unistd_getpid_syscall::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
@ -39,14 +40,14 @@ static void BM_unistd_getpid_syscall(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_unistd_getpid_syscall);
|
|
||||||
|
|
||||||
#if defined(__BIONIC__)
|
#if defined(__BIONIC__)
|
||||||
|
|
||||||
// Stop GCC optimizing out our pure function.
|
// Stop GCC optimizing out our pure function.
|
||||||
/* Must not be static! */ pid_t (*gettid_fp)() = gettid;
|
/* Must not be static! */ pid_t (*gettid_fp)() = gettid;
|
||||||
|
|
||||||
static void BM_unistd_gettid(int iters) {
|
BENCHMARK_NO_ARG(BM_unistd_gettid);
|
||||||
|
void BM_unistd_gettid::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
@ -55,11 +56,11 @@ static void BM_unistd_gettid(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_unistd_gettid);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void BM_unistd_gettid_syscall(int iters) {
|
BENCHMARK_NO_ARG(BM_unistd_gettid_syscall);
|
||||||
|
void BM_unistd_gettid_syscall::Run(int iters) {
|
||||||
StartBenchmarkTiming();
|
StartBenchmarkTiming();
|
||||||
|
|
||||||
for (int i = 0; i < iters; ++i) {
|
for (int i = 0; i < iters; ++i) {
|
||||||
@ -68,4 +69,3 @@ static void BM_unistd_gettid_syscall(int iters) {
|
|||||||
|
|
||||||
StopBenchmarkTiming();
|
StopBenchmarkTiming();
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_unistd_gettid_syscall);
|
|
||||||
|
81
benchmarks/utils.cpp
Normal file
81
benchmarks/utils.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
int Round(int n) {
|
||||||
|
int base = 1;
|
||||||
|
while (base*10 < n) {
|
||||||
|
base *= 10;
|
||||||
|
}
|
||||||
|
if (n < 2*base) {
|
||||||
|
return 2*base;
|
||||||
|
}
|
||||||
|
if (n < 5*base) {
|
||||||
|
return 5*base;
|
||||||
|
}
|
||||||
|
return 10*base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to the code in art, but supporting both binary and decimal prefixes.
|
||||||
|
std::string PrettyInt(long value, size_t base) {
|
||||||
|
if (base != 2 && base != 10) abort();
|
||||||
|
|
||||||
|
uint64_t count = static_cast<uint64_t>(value);
|
||||||
|
bool negative_number = false;
|
||||||
|
if (value < 0) {
|
||||||
|
negative_number = true;
|
||||||
|
count = static_cast<uint64_t>(-value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The byte thresholds at which we display amounts. A count is displayed
|
||||||
|
// in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
|
||||||
|
static const uint64_t kUnitThresholds2[] = {
|
||||||
|
1024*1024*1024 /* Gi */, 2*1024*1024 /* Mi */, 3*1024 /* Ki */, 0,
|
||||||
|
};
|
||||||
|
static const uint64_t kUnitThresholds10[] = {
|
||||||
|
1000*1000*1000 /* G */, 2*1000*1000 /* M */, 3*1000 /* k */, 0,
|
||||||
|
};
|
||||||
|
static const uint64_t kAmountPerUnit2[] = { 1024*1024*1024, 1024*1024, 1024, 1 };
|
||||||
|
static const uint64_t kAmountPerUnit10[] = { 1000*1000*1000, 1000*1000, 1000, 1 };
|
||||||
|
static const char* const kUnitStrings2[] = { "Gi", "Mi", "Ki", "" };
|
||||||
|
static const char* const kUnitStrings10[] = { "G", "M", "k", "" };
|
||||||
|
|
||||||
|
// Which set are we using?
|
||||||
|
const uint64_t* kUnitThresholds = ((base == 2) ? kUnitThresholds2 : kUnitThresholds10);
|
||||||
|
const uint64_t* kAmountPerUnit = ((base == 2) ? kAmountPerUnit2 : kAmountPerUnit10);
|
||||||
|
const char* const* kUnitStrings = ((base == 2) ? kUnitStrings2 : kUnitStrings10);
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
for (; kUnitThresholds[i] != 0; ++i) {
|
||||||
|
if (count >= kUnitThresholds[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char* s = NULL;
|
||||||
|
asprintf(&s, "%s%" PRId64 "%s", (negative_number ? "-" : ""),
|
||||||
|
count / kAmountPerUnit[i], kUnitStrings[i]);
|
||||||
|
std::string result(s);
|
||||||
|
free(s);
|
||||||
|
return result;
|
||||||
|
}
|
26
benchmarks/utils.h
Normal file
26
benchmarks/utils.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BENCHMARKS_UTILS_H
|
||||||
|
#define BENCHMARKS_UTILS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int Round(int n);
|
||||||
|
std::string PrettyInt(long value, size_t base);
|
||||||
|
|
||||||
|
#endif // BENCHMARKS_UTILS_H
|
252
libc/Android.mk
252
libc/Android.mk
@ -85,7 +85,7 @@ libc_common_src_files += \
|
|||||||
bionic/__vsnprintf_chk.cpp \
|
bionic/__vsnprintf_chk.cpp \
|
||||||
bionic/__vsprintf_chk.cpp \
|
bionic/__vsprintf_chk.cpp \
|
||||||
|
|
||||||
libc_bionic_src_files := \
|
libc_bionic_ndk_src_files := \
|
||||||
bionic/abort.cpp \
|
bionic/abort.cpp \
|
||||||
bionic/accept.cpp \
|
bionic/accept.cpp \
|
||||||
bionic/accept4.cpp \
|
bionic/accept4.cpp \
|
||||||
@ -116,16 +116,14 @@ libc_bionic_src_files := \
|
|||||||
bionic/error.cpp \
|
bionic/error.cpp \
|
||||||
bionic/eventfd_read.cpp \
|
bionic/eventfd_read.cpp \
|
||||||
bionic/eventfd_write.cpp \
|
bionic/eventfd_write.cpp \
|
||||||
|
bionic/faccessat.cpp \
|
||||||
bionic/fchmod.cpp \
|
bionic/fchmod.cpp \
|
||||||
bionic/fchmodat.cpp \
|
bionic/fchmodat.cpp \
|
||||||
bionic/ffs.cpp \
|
bionic/ffs.cpp \
|
||||||
bionic/flockfile.cpp \
|
bionic/flockfile.cpp \
|
||||||
bionic/fork.cpp \
|
|
||||||
bionic/fpclassify.cpp \
|
bionic/fpclassify.cpp \
|
||||||
bionic/futimens.cpp \
|
bionic/futimens.cpp \
|
||||||
bionic/getauxval.cpp \
|
|
||||||
bionic/getcwd.cpp \
|
bionic/getcwd.cpp \
|
||||||
bionic/getentropy_linux.c \
|
|
||||||
bionic/gethostname.cpp \
|
bionic/gethostname.cpp \
|
||||||
bionic/getpgrp.cpp \
|
bionic/getpgrp.cpp \
|
||||||
bionic/getpid.cpp \
|
bionic/getpid.cpp \
|
||||||
@ -146,6 +144,7 @@ libc_bionic_src_files := \
|
|||||||
bionic/mbrtoc16.cpp \
|
bionic/mbrtoc16.cpp \
|
||||||
bionic/mbrtoc32.cpp \
|
bionic/mbrtoc32.cpp \
|
||||||
bionic/mbstate.cpp \
|
bionic/mbstate.cpp \
|
||||||
|
bionic/mempcpy.cpp \
|
||||||
bionic/mkdir.cpp \
|
bionic/mkdir.cpp \
|
||||||
bionic/mkfifo.cpp \
|
bionic/mkfifo.cpp \
|
||||||
bionic/mknod.cpp \
|
bionic/mknod.cpp \
|
||||||
@ -160,27 +159,6 @@ libc_bionic_src_files := \
|
|||||||
bionic/posix_fallocate.cpp \
|
bionic/posix_fallocate.cpp \
|
||||||
bionic/posix_madvise.cpp \
|
bionic/posix_madvise.cpp \
|
||||||
bionic/posix_timers.cpp \
|
bionic/posix_timers.cpp \
|
||||||
bionic/pthread_atfork.cpp \
|
|
||||||
bionic/pthread_attr.cpp \
|
|
||||||
bionic/pthread_cond.cpp \
|
|
||||||
bionic/pthread_create.cpp \
|
|
||||||
bionic/pthread_detach.cpp \
|
|
||||||
bionic/pthread_equal.cpp \
|
|
||||||
bionic/pthread_exit.cpp \
|
|
||||||
bionic/pthread_getcpuclockid.cpp \
|
|
||||||
bionic/pthread_getschedparam.cpp \
|
|
||||||
bionic/pthread_gettid_np.cpp \
|
|
||||||
bionic/pthread_internals.cpp \
|
|
||||||
bionic/pthread_join.cpp \
|
|
||||||
bionic/pthread_key.cpp \
|
|
||||||
bionic/pthread_kill.cpp \
|
|
||||||
bionic/pthread_mutex.cpp \
|
|
||||||
bionic/pthread_once.cpp \
|
|
||||||
bionic/pthread_rwlock.cpp \
|
|
||||||
bionic/pthread_self.cpp \
|
|
||||||
bionic/pthread_setname_np.cpp \
|
|
||||||
bionic/pthread_setschedparam.cpp \
|
|
||||||
bionic/pthread_sigmask.cpp \
|
|
||||||
bionic/ptrace.cpp \
|
bionic/ptrace.cpp \
|
||||||
bionic/pty.cpp \
|
bionic/pty.cpp \
|
||||||
bionic/raise.cpp \
|
bionic/raise.cpp \
|
||||||
@ -223,7 +201,6 @@ libc_bionic_src_files := \
|
|||||||
bionic/strtold.cpp \
|
bionic/strtold.cpp \
|
||||||
bionic/stubs.cpp \
|
bionic/stubs.cpp \
|
||||||
bionic/symlink.cpp \
|
bionic/symlink.cpp \
|
||||||
bionic/sysconf.cpp \
|
|
||||||
bionic/sysinfo.cpp \
|
bionic/sysinfo.cpp \
|
||||||
bionic/syslog.cpp \
|
bionic/syslog.cpp \
|
||||||
bionic/sys_siglist.c \
|
bionic/sys_siglist.c \
|
||||||
@ -236,10 +213,27 @@ libc_bionic_src_files := \
|
|||||||
bionic/umount.cpp \
|
bionic/umount.cpp \
|
||||||
bionic/unlink.cpp \
|
bionic/unlink.cpp \
|
||||||
bionic/utimes.cpp \
|
bionic/utimes.cpp \
|
||||||
bionic/vdso.cpp \
|
|
||||||
bionic/wait.cpp \
|
bionic/wait.cpp \
|
||||||
bionic/wchar.cpp \
|
bionic/wchar.cpp \
|
||||||
bionic/wctype.cpp \
|
bionic/wctype.cpp \
|
||||||
|
bionic/wmempcpy.cpp \
|
||||||
|
|
||||||
|
libc_bionic_src_files :=
|
||||||
|
|
||||||
|
# The fork implementation depends on pthread data, so we can't include it in
|
||||||
|
# libc_ndk.a.
|
||||||
|
libc_bionic_src_files += bionic/fork.cpp
|
||||||
|
|
||||||
|
# The data that backs getauxval is initialized in the libc init functions which
|
||||||
|
# are invoked by the linker. If this file is included in libc_ndk.a, only one of
|
||||||
|
# the copies of the global data will be initialized, resulting in nullptr
|
||||||
|
# dereferences.
|
||||||
|
libc_bionic_src_files += bionic/getauxval.cpp
|
||||||
|
|
||||||
|
# These three require getauxval, which isn't available on older platforms.
|
||||||
|
libc_bionic_src_files += bionic/getentropy_linux.c
|
||||||
|
libc_bionic_src_files += bionic/sysconf.cpp
|
||||||
|
libc_bionic_src_files += bionic/vdso.cpp
|
||||||
|
|
||||||
libc_cxa_src_files := \
|
libc_cxa_src_files := \
|
||||||
bionic/__cxa_guard.cpp \
|
bionic/__cxa_guard.cpp \
|
||||||
@ -337,10 +331,13 @@ libc_upstream_openbsd_gdtoa_src_files_64 := \
|
|||||||
$(libc_upstream_openbsd_gdtoa_src_files) \
|
$(libc_upstream_openbsd_gdtoa_src_files) \
|
||||||
upstream-openbsd/lib/libc/gdtoa/strtorQ.c \
|
upstream-openbsd/lib/libc/gdtoa/strtorQ.c \
|
||||||
|
|
||||||
|
# These two depend on getentropy_linux.cpp, which isn't in libc_ndk.a.
|
||||||
libc_upstream_openbsd_src_files := \
|
libc_upstream_openbsd_src_files := \
|
||||||
upstream-openbsd/lib/libc/compat-43/killpg.c \
|
|
||||||
upstream-openbsd/lib/libc/crypt/arc4random.c \
|
upstream-openbsd/lib/libc/crypt/arc4random.c \
|
||||||
upstream-openbsd/lib/libc/crypt/arc4random_uniform.c \
|
upstream-openbsd/lib/libc/crypt/arc4random_uniform.c \
|
||||||
|
|
||||||
|
libc_upstream_openbsd_ndk_src_files := \
|
||||||
|
upstream-openbsd/lib/libc/compat-43/killpg.c \
|
||||||
upstream-openbsd/lib/libc/gen/alarm.c \
|
upstream-openbsd/lib/libc/gen/alarm.c \
|
||||||
upstream-openbsd/lib/libc/gen/ctype_.c \
|
upstream-openbsd/lib/libc/gen/ctype_.c \
|
||||||
upstream-openbsd/lib/libc/gen/daemon.c \
|
upstream-openbsd/lib/libc/gen/daemon.c \
|
||||||
@ -513,6 +510,29 @@ libc_upstream_openbsd_src_files := \
|
|||||||
upstream-openbsd/lib/libc/string/wcsstr.c \
|
upstream-openbsd/lib/libc/string/wcsstr.c \
|
||||||
upstream-openbsd/lib/libc/string/wcswidth.c \
|
upstream-openbsd/lib/libc/string/wcswidth.c \
|
||||||
|
|
||||||
|
libc_pthread_src_files := \
|
||||||
|
bionic/pthread_atfork.cpp \
|
||||||
|
bionic/pthread_attr.cpp \
|
||||||
|
bionic/pthread_cond.cpp \
|
||||||
|
bionic/pthread_create.cpp \
|
||||||
|
bionic/pthread_detach.cpp \
|
||||||
|
bionic/pthread_equal.cpp \
|
||||||
|
bionic/pthread_exit.cpp \
|
||||||
|
bionic/pthread_getcpuclockid.cpp \
|
||||||
|
bionic/pthread_getschedparam.cpp \
|
||||||
|
bionic/pthread_gettid_np.cpp \
|
||||||
|
bionic/pthread_internals.cpp \
|
||||||
|
bionic/pthread_join.cpp \
|
||||||
|
bionic/pthread_key.cpp \
|
||||||
|
bionic/pthread_kill.cpp \
|
||||||
|
bionic/pthread_mutex.cpp \
|
||||||
|
bionic/pthread_once.cpp \
|
||||||
|
bionic/pthread_rwlock.cpp \
|
||||||
|
bionic/pthread_self.cpp \
|
||||||
|
bionic/pthread_setname_np.cpp \
|
||||||
|
bionic/pthread_setschedparam.cpp \
|
||||||
|
bionic/pthread_sigmask.cpp \
|
||||||
|
|
||||||
libc_arch_static_src_files := \
|
libc_arch_static_src_files := \
|
||||||
bionic/dl_iterate_phdr_static.cpp \
|
bionic/dl_iterate_phdr_static.cpp \
|
||||||
|
|
||||||
@ -785,6 +805,51 @@ $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_netbsd_src_files
|
|||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# libc_openbsd_ndk.a - upstream OpenBSD C library code
|
||||||
|
# that can be safely included in the libc_ndk.a (doesn't
|
||||||
|
# contain any troublesome global data or constructors).
|
||||||
|
# ========================================================
|
||||||
|
#
|
||||||
|
# These files are built with the openbsd-compat.h header file
|
||||||
|
# automatically included.
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(libc_upstream_openbsd_ndk_src_files)
|
||||||
|
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
||||||
|
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
||||||
|
LOCAL_CLANG := false
|
||||||
|
else
|
||||||
|
LOCAL_CLANG := $(use_clang)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := \
|
||||||
|
$(libc_common_cflags) \
|
||||||
|
-Wno-sign-compare \
|
||||||
|
-Wno-uninitialized \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-include openbsd-compat.h \
|
||||||
|
|
||||||
|
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
||||||
|
LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
||||||
|
LOCAL_C_INCLUDES := $(libc_common_c_includes) \
|
||||||
|
$(LOCAL_PATH)/private \
|
||||||
|
$(LOCAL_PATH)/upstream-openbsd/android/include \
|
||||||
|
$(LOCAL_PATH)/upstream-openbsd/lib/libc/include \
|
||||||
|
$(LOCAL_PATH)/upstream-openbsd/lib/libc/gdtoa/ \
|
||||||
|
|
||||||
|
LOCAL_MODULE := libc_openbsd_ndk
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||||
|
LOCAL_CXX_STL := none
|
||||||
|
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||||
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
|
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
||||||
|
|
||||||
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
# ========================================================
|
# ========================================================
|
||||||
# libc_openbsd.a - upstream OpenBSD C library code
|
# libc_openbsd.a - upstream OpenBSD C library code
|
||||||
# ========================================================
|
# ========================================================
|
||||||
@ -899,11 +964,80 @@ LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
|||||||
LOCAL_ADDRESS_SANITIZER := false
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
||||||
|
|
||||||
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# libc_bionic_ndk.a - The portions of libc_bionic that can
|
||||||
|
# be safely used in libc_ndk.a (no troublesome global data
|
||||||
|
# or constructors).
|
||||||
|
# ========================================================
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(libc_bionic_ndk_src_files)
|
||||||
|
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||||
|
-Wframe-larger-than=2048 \
|
||||||
|
|
||||||
|
# ssse3-strcmp-slm.S does not compile with Clang.
|
||||||
|
LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as
|
||||||
|
|
||||||
|
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||||
|
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||||
|
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
||||||
|
|
||||||
|
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
||||||
|
LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast
|
||||||
|
LOCAL_C_INCLUDES := $(libc_common_c_includes) bionic/libstdc++/include
|
||||||
|
LOCAL_MODULE := libc_bionic_ndk
|
||||||
|
LOCAL_CLANG := $(use_clang)
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||||
|
LOCAL_CXX_STL := none
|
||||||
|
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||||
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
|
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
||||||
|
|
||||||
$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
|
||||||
$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_bionic_src_files))
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_bionic_src_files))
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# libc_pthread.a - pthreads parts that previously lived in
|
||||||
|
# libc_bionic.a. Relocated to their own library because
|
||||||
|
# they can't be included in libc_ndk.a (as they layout of
|
||||||
|
# pthread_t has changed over the years and has ABI
|
||||||
|
# compatibility issues).
|
||||||
|
# ========================================================
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(libc_pthread_src_files)
|
||||||
|
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||||
|
-Wframe-larger-than=2048 \
|
||||||
|
|
||||||
|
# ssse3-strcmp-slm.S does not compile with Clang.
|
||||||
|
LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as
|
||||||
|
|
||||||
|
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||||
|
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||||
|
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
||||||
|
|
||||||
|
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
||||||
|
LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast
|
||||||
|
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
||||||
|
LOCAL_MODULE := libc_pthread
|
||||||
|
LOCAL_CLANG := $(use_clang)
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||||
|
LOCAL_CXX_STL := none
|
||||||
|
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||||
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
|
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
# ========================================================
|
# ========================================================
|
||||||
# libc_cxa.a - Things traditionally in libstdc++
|
# libc_cxa.a - Things traditionally in libstdc++
|
||||||
# ========================================================
|
# ========================================================
|
||||||
@ -992,9 +1126,56 @@ include $(BUILD_STATIC_LIBRARY)
|
|||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_MODULE := libc_ndk
|
LOCAL_MODULE := libc_ndk
|
||||||
LOCAL_WHOLE_STATIC_LIBRARIES := libc_syscalls libm
|
LOCAL_CLANG := $(use_clang)
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
|
||||||
|
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
||||||
|
LOCAL_CFLAGS := $(libc_common_cflags) -fvisibility=hidden -O0
|
||||||
|
LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
||||||
|
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
||||||
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
|
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
||||||
|
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
$(libc_common_src_files) \
|
||||||
|
$(libc_arch_dynamic_src_files) \
|
||||||
|
$(libc_ndk_stub_src_files) \
|
||||||
|
bionic/malloc_debug_common.cpp \
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES_arm += \
|
||||||
|
arch-common/bionic/crtbegin_so.c \
|
||||||
|
arch-arm/bionic/atexit_legacy.c \
|
||||||
|
arch-common/bionic/crtend_so.S \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||||
|
-DLIBC_STATIC \
|
||||||
|
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||||
|
libc_bionic_ndk \
|
||||||
|
libc_cxa \
|
||||||
|
libc_freebsd \
|
||||||
|
libc_gdtoa \
|
||||||
|
libc_malloc \
|
||||||
|
libc_netbsd \
|
||||||
|
libc_openbsd_ndk \
|
||||||
|
libc_stack_protector \
|
||||||
|
libc_syscalls \
|
||||||
|
libc_tzcode \
|
||||||
|
libm \
|
||||||
|
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi
|
||||||
|
LOCAL_CXX_STL := none
|
||||||
|
|
||||||
|
ifneq ($(MALLOC_IMPL),dlmalloc)
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
|
||||||
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_common_src_files))
|
||||||
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_arch_dynamic_src_files))
|
||||||
|
$(eval $(call patch-up-arch-specific-flags,LOCAL_ASFLAGS,LOCAL_CFLAGS))
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
# ========================================================
|
# ========================================================
|
||||||
@ -1013,6 +1194,7 @@ LOCAL_CLANG := $(use_clang)
|
|||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||||
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||||
libc_bionic \
|
libc_bionic \
|
||||||
|
libc_bionic_ndk \
|
||||||
libc_cxa \
|
libc_cxa \
|
||||||
libc_dns \
|
libc_dns \
|
||||||
libc_freebsd \
|
libc_freebsd \
|
||||||
@ -1020,6 +1202,8 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \
|
|||||||
libc_malloc \
|
libc_malloc \
|
||||||
libc_netbsd \
|
libc_netbsd \
|
||||||
libc_openbsd \
|
libc_openbsd \
|
||||||
|
libc_openbsd_ndk \
|
||||||
|
libc_pthread \
|
||||||
libc_stack_protector \
|
libc_stack_protector \
|
||||||
libc_syscalls \
|
libc_syscalls \
|
||||||
libc_tzcode \
|
libc_tzcode \
|
||||||
@ -1140,6 +1324,10 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_CFLAGS := $(libc_common_cflags)
|
LOCAL_CFLAGS := $(libc_common_cflags)
|
||||||
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
||||||
LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
||||||
|
|
||||||
|
# TODO: This is to work around b/19059885. Remove after root cause is fixed
|
||||||
|
LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
$(libc_arch_dynamic_src_files) \
|
$(libc_arch_dynamic_src_files) \
|
||||||
@ -1290,6 +1478,10 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_C_INCLUDES := $(libc_common_c_includes) bionic/libstdc++/include
|
LOCAL_C_INCLUDES := $(libc_common_c_includes) bionic/libstdc++/include
|
||||||
LOCAL_CFLAGS := $(libc_common_cflags)
|
LOCAL_CFLAGS := $(libc_common_cflags)
|
||||||
LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
||||||
|
|
||||||
|
# TODO: This is to work around b/19059885. Remove after root cause is fixed
|
||||||
|
LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(libstdcxx_common_src_files)
|
LOCAL_SRC_FILES := $(libstdcxx_common_src_files)
|
||||||
LOCAL_MODULE:= libstdc++
|
LOCAL_MODULE:= libstdc++
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
|
||||||
|
@ -130,7 +130,7 @@ int fremovexattr(int, const char*) all
|
|||||||
int __getdents64:getdents64(unsigned int, struct dirent*, unsigned int) arm,arm64,mips,mips64,x86,x86_64
|
int __getdents64:getdents64(unsigned int, struct dirent*, unsigned int) arm,arm64,mips,mips64,x86,x86_64
|
||||||
|
|
||||||
int __openat:openat(int, const char*, int, mode_t) all
|
int __openat:openat(int, const char*, int, mode_t) all
|
||||||
int faccessat(int, const char*, int, int) all
|
int ___faccessat:faccessat(int, const char*, int) all
|
||||||
int ___fchmodat:fchmodat(int, const char*, mode_t) all
|
int ___fchmodat:fchmodat(int, const char*, mode_t) all
|
||||||
int fchownat(int, const char*, uid_t, gid_t, int) all
|
int fchownat(int, const char*, uid_t, gid_t, int) all
|
||||||
int fstatat64|fstatat:fstatat64(int, const char*, struct stat*, int) arm,mips,x86
|
int fstatat64|fstatat:fstatat64(int, const char*, struct stat*, int) arm,mips,x86
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
libc_bionic_src_files_arm += \
|
libc_bionic_src_files_arm += \
|
||||||
bionic/memchr.c \
|
|
||||||
bionic/memrchr.c \
|
|
||||||
bionic/strchr.cpp \
|
bionic/strchr.cpp \
|
||||||
bionic/strnlen.c \
|
bionic/strnlen.c \
|
||||||
bionic/strrchr.cpp \
|
bionic/strrchr.cpp \
|
||||||
@ -22,6 +20,8 @@ libc_freebsd_src_files_arm += \
|
|||||||
upstream-freebsd/lib/libc/string/wmemmove.c \
|
upstream-freebsd/lib/libc/string/wmemmove.c \
|
||||||
|
|
||||||
libc_openbsd_src_files_arm += \
|
libc_openbsd_src_files_arm += \
|
||||||
|
upstream-openbsd/lib/libc/string/memchr.c \
|
||||||
|
upstream-openbsd/lib/libc/string/memrchr.c \
|
||||||
upstream-openbsd/lib/libc/string/stpncpy.c \
|
upstream-openbsd/lib/libc/string/stpncpy.c \
|
||||||
upstream-openbsd/lib/libc/string/strlcat.c \
|
upstream-openbsd/lib/libc/string/strlcat.c \
|
||||||
upstream-openbsd/lib/libc/string/strlcpy.c \
|
upstream-openbsd/lib/libc/string/strlcpy.c \
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(faccessat)
|
ENTRY(___faccessat)
|
||||||
mov ip, r7
|
mov ip, r7
|
||||||
ldr r7, =__NR_faccessat
|
ldr r7, =__NR_faccessat
|
||||||
swi #0
|
swi #0
|
||||||
@ -11,4 +11,5 @@ ENTRY(faccessat)
|
|||||||
bxls lr
|
bxls lr
|
||||||
neg r0, r0
|
neg r0, r0
|
||||||
b __set_errno_internal
|
b __set_errno_internal
|
||||||
END(faccessat)
|
END(___faccessat)
|
||||||
|
.hidden ___faccessat
|
@ -8,7 +8,6 @@ libc_bionic_src_files_arm64 += \
|
|||||||
bionic/__memset_chk.cpp \
|
bionic/__memset_chk.cpp \
|
||||||
bionic/__strcpy_chk.cpp \
|
bionic/__strcpy_chk.cpp \
|
||||||
bionic/__strcat_chk.cpp \
|
bionic/__strcat_chk.cpp \
|
||||||
bionic/memrchr.c \
|
|
||||||
bionic/strrchr.cpp \
|
bionic/strrchr.cpp \
|
||||||
|
|
||||||
libc_freebsd_src_files_arm64 += \
|
libc_freebsd_src_files_arm64 += \
|
||||||
@ -21,6 +20,7 @@ libc_freebsd_src_files_arm64 += \
|
|||||||
upstream-freebsd/lib/libc/string/wmemcmp.c \
|
upstream-freebsd/lib/libc/string/wmemcmp.c \
|
||||||
|
|
||||||
libc_openbsd_src_files_arm64 += \
|
libc_openbsd_src_files_arm64 += \
|
||||||
|
upstream-openbsd/lib/libc/string/memrchr.c \
|
||||||
upstream-openbsd/lib/libc/string/stpncpy.c \
|
upstream-openbsd/lib/libc/string/stpncpy.c \
|
||||||
upstream-openbsd/lib/libc/string/strcat.c \
|
upstream-openbsd/lib/libc/string/strcat.c \
|
||||||
upstream-openbsd/lib/libc/string/strlcat.c \
|
upstream-openbsd/lib/libc/string/strlcat.c \
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(faccessat)
|
ENTRY(___faccessat)
|
||||||
mov x8, __NR_faccessat
|
mov x8, __NR_faccessat
|
||||||
svc #0
|
svc #0
|
||||||
|
|
||||||
@ -11,4 +11,5 @@ ENTRY(faccessat)
|
|||||||
b.hi __set_errno_internal
|
b.hi __set_errno_internal
|
||||||
|
|
||||||
ret
|
ret
|
||||||
END(faccessat)
|
END(___faccessat)
|
||||||
|
.hidden ___faccessat
|
@ -1,3 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014-2015 The Android Open Source Project
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2002 Opsycon AB (www.opsycon.se / www.opsycon.com)
|
* Copyright (c) 2001-2002 Opsycon AB (www.opsycon.se / www.opsycon.com)
|
||||||
*
|
*
|
||||||
@ -94,23 +121,31 @@
|
|||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
#include <machine/setjmp.h>
|
#include <machine/setjmp.h>
|
||||||
|
|
||||||
/* On Mips32, jmpbuf begins with optional 4-byte filler so that
|
/* jmpbuf is declared to users as an array of longs, which is only
|
||||||
* all saved FP regs are aligned on 8-byte boundary, despite this whole
|
* 4-byte aligned in 32-bit builds. The Mips jmpbuf begins with a
|
||||||
* struct being mis-declared to users as an array of (4-byte) longs.
|
* dynamically-sized 0- or 4-byte unused filler so that double-prec FP regs
|
||||||
* All the following offsets are then from the rounded-up base addr
|
* are saved to 8-byte-aligned mem cells.
|
||||||
|
* All the following jmpbuf offsets are from the rounded-DOWN base addr.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Fields of same size on all MIPS abis: */
|
/* Fields of same size on all MIPS abis: */
|
||||||
#define SC_MAGIC (0*4) /* 4 bytes, identify jmpbuf */
|
/* field: byte offset: size: */
|
||||||
#define SC_MASK (1*4) /* 4 bytes, saved signal mask */
|
/* dynam filler (0*4) 0-4 bytes of rounddown filler, DON'T TOUCH!!
|
||||||
#define SC_FPSR (2*4) /* 4 bytes, floating point control/status reg */
|
often overlays user storage!! */
|
||||||
/* filler2 (3*4) 4 bytes, pad to 8-byte boundary */
|
#define SC_MAGIC_OFFSET (1*4) /* 4 bytes, identify jmpbuf, first actual field */
|
||||||
|
#define SC_FLAG_OFFSET (2*4) /* 4 bytes, savesigs flag */
|
||||||
|
#define SC_FPSR_OFFSET (3*4) /* 4 bytes, floating point control/status reg */
|
||||||
|
/* following fields are 8-byte aligned */
|
||||||
|
#define SC_MASK_OFFSET (4*4) /* 16 bytes, mips32/mips64 version of sigset_t */
|
||||||
|
#define SC_SPARE_OFFSET (8*4) /* 8 bytes, reserved for future uses */
|
||||||
|
|
||||||
/* Registers that are 4-byte on mips32 o32, and 8-byte on mips64 n64 abi */
|
/* Registers that are 4-byte on mips32 o32, and 8-byte on mips64 n64 abi */
|
||||||
#define SC_REGS_SAVED 12 /* ra,gp,sp,s0-s8 */
|
#define SC_REGS_OFFSET (10*4) /* SC_REGS_BYTES */
|
||||||
#define SC_REGS (4*4) /* SC_REGS_SAVED*REGSZ bytes */
|
#define SC_REGS_SAVED 12 /*regs*/ /* ra,s0-s8,gp,sp */
|
||||||
|
#define SC_REGS_BYTES (SC_REGS_SAVED*REGSZ)
|
||||||
|
#define SC_REGS SC_REGS_OFFSET
|
||||||
|
|
||||||
/* Floating pt registers are 8-bytes on all abis,
|
/* Double floating pt registers are 8-bytes on all abis,
|
||||||
* but the number of saved fp regs varies for o32/n32 versus n64 abis:
|
* but the number of saved fp regs varies for o32/n32 versus n64 abis:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -120,22 +155,20 @@
|
|||||||
#define SC_FPREGS_SAVED 6 /* even fp regs f20,f22,f24,f26,f28,f30 */
|
#define SC_FPREGS_SAVED 6 /* even fp regs f20,f22,f24,f26,f28,f30 */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SC_FPREGS (SC_REGS + SC_REGS_SAVED*REGSZ) /* SC_FPREGS_SAVED*REGSZ_FP bytes */
|
#define SC_FPREGS_OFFSET (SC_REGS_OFFSET + SC_REGS_BYTES) /* SC_FPREGS_BYTES */
|
||||||
|
#define SC_FPREGS_BYTES (SC_FPREGS_SAVED*REGSZ_FP)
|
||||||
|
#define SC_FPREGS SC_FPREGS_OFFSET
|
||||||
|
|
||||||
#define SC_BYTES (SC_FPREGS + SC_FPREGS_SAVED*REGSZ_FP)
|
#define SC_TOTAL_BYTES (SC_FPREGS_OFFSET + SC_FPREGS_BYTES)
|
||||||
#define SC_LONGS (SC_BYTES/REGSZ)
|
#define SC_TOTAL_LONGS (SC_TOTAL_BYTES/REGSZ)
|
||||||
|
|
||||||
#ifdef __LP64__
|
#if SC_TOTAL_LONGS > _JBLEN
|
||||||
/* SC_LONGS is 22, so _JBLEN should be 22 or larger */
|
#error _JBLEN is too small
|
||||||
#else
|
|
||||||
/* SC_LONGS is 28, but must also allocate dynamic-roundup filler.
|
|
||||||
so _JBLEN should be 29 or larger */
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _setjmp, _longjmp (restoring signal state)
|
|
||||||
*
|
*
|
||||||
* GPOFF and FRAMESIZE must be the same for both _setjmp and _longjmp!
|
* GPOFF and FRAMESIZE must be the same for all setjmp/longjmp routines
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -145,30 +178,33 @@ A0OFF= FRAMESZ-3*REGSZ
|
|||||||
GPOFF= FRAMESZ-2*REGSZ
|
GPOFF= FRAMESZ-2*REGSZ
|
||||||
RAOFF= FRAMESZ-1*REGSZ
|
RAOFF= FRAMESZ-1*REGSZ
|
||||||
|
|
||||||
NON_LEAF(setjmp, FRAMESZ, ra)
|
NON_LEAF(sigsetjmp, FRAMESZ, ra)
|
||||||
.mask 0x80000000, RAOFF
|
.mask 0x80000000, RAOFF
|
||||||
PTR_SUBU sp, FRAMESZ # allocate stack frame
|
PTR_SUBU sp, FRAMESZ # allocate stack frame
|
||||||
SETUP_GP64(GPOFF, setjmp)
|
SETUP_GP64(GPOFF, sigsetjmp)
|
||||||
SAVE_GP(GPOFF)
|
SAVE_GP(GPOFF)
|
||||||
.set reorder
|
.set reorder
|
||||||
|
|
||||||
|
setjmp_common:
|
||||||
#ifndef __LP64__
|
#ifndef __LP64__
|
||||||
addiu a0, 7 # roundup jmpbuf addr to 8-byte boundary
|
li t0, ~7
|
||||||
li t0, ~7
|
and a0, t0 # round jmpbuf addr DOWN to 8-byte boundary
|
||||||
and a0, t0
|
|
||||||
#endif
|
#endif
|
||||||
|
sw a1, SC_FLAG_OFFSET(a0) # save savesigs flag
|
||||||
|
beqz a1, 1f # do saving of signal mask?
|
||||||
|
|
||||||
REG_S ra, RAOFF(sp) # save state
|
REG_S ra, RAOFF(sp) # spill state
|
||||||
REG_S a0, A0OFF(sp)
|
REG_S a0, A0OFF(sp)
|
||||||
move a0, zero # get current signal mask
|
# call sigprocmask(int how ignored, sigset_t* null, sigset_t* SC_MASK(a0)):
|
||||||
jal sigblock
|
LA a2, SC_MASK_OFFSET(a0) # gets current signal mask
|
||||||
|
li a0, 0 # how; ignored when new mask is null
|
||||||
|
li a1, 0 # null new mask
|
||||||
|
jal sigprocmask # get current signal mask
|
||||||
REG_L a0, A0OFF(sp)
|
REG_L a0, A0OFF(sp)
|
||||||
REG_L ra, RAOFF(sp)
|
REG_L ra, RAOFF(sp)
|
||||||
|
1:
|
||||||
REG_S v0, SC_MASK(a0) # save sc_mask = sigblock(0)
|
|
||||||
|
|
||||||
li v0, 0xACEDBADE # sigcontext magic number
|
li v0, 0xACEDBADE # sigcontext magic number
|
||||||
sw v0, SC_MAGIC(a0)
|
sw v0, SC_MAGIC_OFFSET(a0)
|
||||||
# callee-saved long-sized regs:
|
# callee-saved long-sized regs:
|
||||||
REG_S ra, SC_REGS+0*REGSZ(a0)
|
REG_S ra, SC_REGS+0*REGSZ(a0)
|
||||||
REG_S s0, SC_REGS+1*REGSZ(a0)
|
REG_S s0, SC_REGS+1*REGSZ(a0)
|
||||||
@ -181,9 +217,9 @@ NON_LEAF(setjmp, FRAMESZ, ra)
|
|||||||
REG_S s7, SC_REGS+8*REGSZ(a0)
|
REG_S s7, SC_REGS+8*REGSZ(a0)
|
||||||
REG_S s8, SC_REGS+9*REGSZ(a0)
|
REG_S s8, SC_REGS+9*REGSZ(a0)
|
||||||
REG_L v0, GPOFF(sp)
|
REG_L v0, GPOFF(sp)
|
||||||
REG_S v0, SC_REGS+10*REGSZ(a0)
|
REG_S v0, SC_REGS+10*REGSZ(a0) # save gp
|
||||||
PTR_ADDU v0, sp, FRAMESZ
|
PTR_ADDU v0, sp, FRAMESZ
|
||||||
REG_S v0, SC_REGS+11*REGSZ(a0)
|
REG_S v0, SC_REGS+11*REGSZ(a0) # save orig sp
|
||||||
|
|
||||||
cfc1 v0, $31
|
cfc1 v0, $31
|
||||||
|
|
||||||
@ -199,7 +235,7 @@ NON_LEAF(setjmp, FRAMESZ, ra)
|
|||||||
s.d $f31, SC_FPREGS+7*REGSZ_FP(a0)
|
s.d $f31, SC_FPREGS+7*REGSZ_FP(a0)
|
||||||
#else
|
#else
|
||||||
# callee-saved fp regs on mips o32 ABI are
|
# callee-saved fp regs on mips o32 ABI are
|
||||||
# the even-numbered fp regs $f20,$f22,...$f30
|
# the even-numbered double fp regs $f20,$f22,...$f30
|
||||||
s.d $f20, SC_FPREGS+0*REGSZ_FP(a0)
|
s.d $f20, SC_FPREGS+0*REGSZ_FP(a0)
|
||||||
s.d $f22, SC_FPREGS+1*REGSZ_FP(a0)
|
s.d $f22, SC_FPREGS+1*REGSZ_FP(a0)
|
||||||
s.d $f24, SC_FPREGS+2*REGSZ_FP(a0)
|
s.d $f24, SC_FPREGS+2*REGSZ_FP(a0)
|
||||||
@ -207,37 +243,68 @@ NON_LEAF(setjmp, FRAMESZ, ra)
|
|||||||
s.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
|
s.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
|
||||||
s.d $f30, SC_FPREGS+5*REGSZ_FP(a0)
|
s.d $f30, SC_FPREGS+5*REGSZ_FP(a0)
|
||||||
#endif
|
#endif
|
||||||
sw v0, SC_FPSR(a0)
|
sw v0, SC_FPSR_OFFSET(a0)
|
||||||
move v0, zero
|
move v0, zero
|
||||||
RESTORE_GP64
|
RESTORE_GP64
|
||||||
PTR_ADDU sp, FRAMESZ
|
PTR_ADDU sp, FRAMESZ
|
||||||
j ra
|
j ra
|
||||||
END(setjmp)
|
END(sigsetjmp)
|
||||||
|
|
||||||
NON_LEAF(longjmp, FRAMESZ, ra)
|
|
||||||
|
# Alternate entry points:
|
||||||
|
|
||||||
|
NON_LEAF(setjmp, FRAMESZ, ra)
|
||||||
.mask 0x80000000, RAOFF
|
.mask 0x80000000, RAOFF
|
||||||
PTR_SUBU sp, FRAMESZ
|
PTR_SUBU sp, FRAMESZ
|
||||||
SETUP_GP64(GPOFF, longjmp)
|
SETUP_GP64(GPOFF, setjmp) # can't share sigsetjmp's gp code
|
||||||
|
SAVE_GP(GPOFF)
|
||||||
|
.set reorder
|
||||||
|
|
||||||
|
li a1, 1 # save/restore signals state
|
||||||
|
b setjmp_common # tail call
|
||||||
|
END(setjmp)
|
||||||
|
|
||||||
|
|
||||||
|
NON_LEAF(_setjmp, FRAMESZ, ra)
|
||||||
|
.mask 0x80000000, RAOFF
|
||||||
|
PTR_SUBU sp, FRAMESZ
|
||||||
|
SETUP_GP64(GPOFF, _setjmp) # can't share sigsetjmp's gp code
|
||||||
|
SAVE_GP(GPOFF)
|
||||||
|
.set reorder
|
||||||
|
|
||||||
|
li a1, 0 # don't save/restore signals
|
||||||
|
b setjmp_common # tail call
|
||||||
|
END(_setjmp)
|
||||||
|
|
||||||
|
|
||||||
|
NON_LEAF(siglongjmp, FRAMESZ, ra)
|
||||||
|
.mask 0x80000000, RAOFF
|
||||||
|
PTR_SUBU sp, FRAMESZ
|
||||||
|
SETUP_GP64(GPOFF, siglongjmp)
|
||||||
SAVE_GP(GPOFF)
|
SAVE_GP(GPOFF)
|
||||||
.set reorder
|
.set reorder
|
||||||
|
|
||||||
#ifndef __LP64__
|
#ifndef __LP64__
|
||||||
addiu a0, 7 # roundup jmpbuf addr to 8-byte boundary
|
li t0, ~7
|
||||||
li t0, ~7
|
and a0, t0 # round jmpbuf addr DOWN to 8-byte boundary
|
||||||
and a0, t0
|
|
||||||
#endif
|
#endif
|
||||||
|
lw v0, SC_MAGIC_OFFSET(a0)
|
||||||
|
li t0, 0xACEDBADE
|
||||||
|
bne v0, t0, longjmp_botch # jump if error
|
||||||
|
|
||||||
REG_S a1, A1OFF(sp)
|
lw t0, SC_FLAG_OFFSET(a0) # get savesigs flag
|
||||||
|
beqz t0, 1f # restore signal mask?
|
||||||
|
|
||||||
|
REG_S a1, A1OFF(sp) # temp spill
|
||||||
REG_S a0, A0OFF(sp)
|
REG_S a0, A0OFF(sp)
|
||||||
lw a0, SC_MASK(a0)
|
# call sigprocmask(int how SIG_SETMASK, sigset_t* SC_MASK(a0), sigset_t* null):
|
||||||
jal sigsetmask
|
LA a1, SC_MASK_OFFSET(a0) # signals being restored
|
||||||
|
li a0, 3 # mips SIG_SETMASK
|
||||||
|
li a2, 0 # null
|
||||||
|
jal sigprocmask # restore signal mask
|
||||||
REG_L a0, A0OFF(sp)
|
REG_L a0, A0OFF(sp)
|
||||||
REG_L a1, A1OFF(sp)
|
REG_L a1, A1OFF(sp)
|
||||||
|
1:
|
||||||
lw v0, SC_MAGIC(a0)
|
|
||||||
li t0, 0xACEDBADE
|
|
||||||
bne v0, t0, longjmp_botch # jump if error
|
|
||||||
|
|
||||||
# callee-saved long-sized regs:
|
# callee-saved long-sized regs:
|
||||||
REG_L ra, SC_REGS+0*REGSZ(a0)
|
REG_L ra, SC_REGS+0*REGSZ(a0)
|
||||||
REG_L s0, SC_REGS+1*REGSZ(a0)
|
REG_L s0, SC_REGS+1*REGSZ(a0)
|
||||||
@ -252,8 +319,8 @@ NON_LEAF(longjmp, FRAMESZ, ra)
|
|||||||
REG_L gp, SC_REGS+10*REGSZ(a0)
|
REG_L gp, SC_REGS+10*REGSZ(a0)
|
||||||
REG_L sp, SC_REGS+11*REGSZ(a0)
|
REG_L sp, SC_REGS+11*REGSZ(a0)
|
||||||
|
|
||||||
lw v0, SC_FPSR(a0)
|
lw v0, SC_FPSR_OFFSET(a0)
|
||||||
ctc1 v0, $31
|
ctc1 v0, $31 # restore old fr mode before fp values
|
||||||
#ifdef __LP64__
|
#ifdef __LP64__
|
||||||
# callee-saved fp regs on mips n64 ABI are $f24..$f31
|
# callee-saved fp regs on mips n64 ABI are $f24..$f31
|
||||||
l.d $f24, SC_FPREGS+0*REGSZ_FP(a0)
|
l.d $f24, SC_FPREGS+0*REGSZ_FP(a0)
|
||||||
@ -266,7 +333,7 @@ NON_LEAF(longjmp, FRAMESZ, ra)
|
|||||||
l.d $f31, SC_FPREGS+7*REGSZ_FP(a0)
|
l.d $f31, SC_FPREGS+7*REGSZ_FP(a0)
|
||||||
#else
|
#else
|
||||||
# callee-saved fp regs on mips o32 ABI are
|
# callee-saved fp regs on mips o32 ABI are
|
||||||
# the even-numbered fp regs $f20,$f22,...$f30
|
# the even-numbered double fp regs $f20,$f22,...$f30
|
||||||
l.d $f20, SC_FPREGS+0*REGSZ_FP(a0)
|
l.d $f20, SC_FPREGS+0*REGSZ_FP(a0)
|
||||||
l.d $f22, SC_FPREGS+1*REGSZ_FP(a0)
|
l.d $f22, SC_FPREGS+1*REGSZ_FP(a0)
|
||||||
l.d $f24, SC_FPREGS+2*REGSZ_FP(a0)
|
l.d $f24, SC_FPREGS+2*REGSZ_FP(a0)
|
||||||
@ -278,192 +345,19 @@ NON_LEAF(longjmp, FRAMESZ, ra)
|
|||||||
li a1, 1 # never return 0!
|
li a1, 1 # never return 0!
|
||||||
1:
|
1:
|
||||||
move v0, a1
|
move v0, a1
|
||||||
j ra
|
j ra # return to setjmp call site
|
||||||
|
|
||||||
longjmp_botch:
|
longjmp_botch:
|
||||||
jal longjmperror
|
jal longjmperror
|
||||||
jal abort
|
jal abort
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
END(longjmp)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* _setjmp, _longjmp (not restoring signal state)
|
|
||||||
*
|
|
||||||
* GPOFF and FRAMESIZE must be the same for both _setjmp and _longjmp!
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
FRAMESZ= MKFSIZ(0,4)
|
|
||||||
GPOFF= FRAMESZ-2*REGSZ
|
|
||||||
|
|
||||||
LEAF(_setjmp, FRAMESZ)
|
|
||||||
PTR_SUBU sp, FRAMESZ
|
|
||||||
SETUP_GP64(GPOFF, _setjmp)
|
|
||||||
SAVE_GP(GPOFF)
|
|
||||||
.set reorder
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
addiu a0, 7 # roundup jmpbuf addr to 8-byte boundary
|
|
||||||
li t0, ~7
|
|
||||||
and a0, t0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
# SC_MASK is unused here
|
|
||||||
|
|
||||||
li v0, 0xACEDBADE # sigcontext magic number
|
|
||||||
sw v0, SC_MAGIC(a0)
|
|
||||||
# callee-saved long-sized regs:
|
|
||||||
REG_S ra, SC_REGS+0*REGSZ(a0)
|
|
||||||
REG_S s0, SC_REGS+1*REGSZ(a0)
|
|
||||||
REG_S s1, SC_REGS+2*REGSZ(a0)
|
|
||||||
REG_S s2, SC_REGS+3*REGSZ(a0)
|
|
||||||
REG_S s3, SC_REGS+4*REGSZ(a0)
|
|
||||||
REG_S s4, SC_REGS+5*REGSZ(a0)
|
|
||||||
REG_S s5, SC_REGS+6*REGSZ(a0)
|
|
||||||
REG_S s6, SC_REGS+7*REGSZ(a0)
|
|
||||||
REG_S s7, SC_REGS+8*REGSZ(a0)
|
|
||||||
REG_S s8, SC_REGS+9*REGSZ(a0)
|
|
||||||
REG_L v0, GPOFF(sp)
|
|
||||||
REG_S v0, SC_REGS+10*REGSZ(a0)
|
|
||||||
PTR_ADDU v0, sp, FRAMESZ
|
|
||||||
REG_S v0, SC_REGS+11*REGSZ(a0)
|
|
||||||
|
|
||||||
cfc1 v0, $31
|
|
||||||
|
|
||||||
#ifdef __LP64__
|
|
||||||
# callee-saved fp regs on mips n64 ABI are $f24..$f31
|
|
||||||
s.d $f24, SC_FPREGS+0*REGSZ_FP(a0)
|
|
||||||
s.d $f25, SC_FPREGS+1*REGSZ_FP(a0)
|
|
||||||
s.d $f26, SC_FPREGS+2*REGSZ_FP(a0)
|
|
||||||
s.d $f27, SC_FPREGS+3*REGSZ_FP(a0)
|
|
||||||
s.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
|
|
||||||
s.d $f29, SC_FPREGS+5*REGSZ_FP(a0)
|
|
||||||
s.d $f30, SC_FPREGS+6*REGSZ_FP(a0)
|
|
||||||
s.d $f31, SC_FPREGS+7*REGSZ_FP(a0)
|
|
||||||
#else
|
|
||||||
# callee-saved fp regs on mips o32 ABI are
|
|
||||||
# the even-numbered fp regs $f20,$f22,...$f30
|
|
||||||
s.d $f20, SC_FPREGS+0*REGSZ_FP(a0)
|
|
||||||
s.d $f22, SC_FPREGS+1*REGSZ_FP(a0)
|
|
||||||
s.d $f24, SC_FPREGS+2*REGSZ_FP(a0)
|
|
||||||
s.d $f26, SC_FPREGS+3*REGSZ_FP(a0)
|
|
||||||
s.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
|
|
||||||
s.d $f30, SC_FPREGS+5*REGSZ_FP(a0)
|
|
||||||
#endif
|
|
||||||
sw v0, SC_FPSR(a0)
|
|
||||||
move v0, zero
|
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
j ra
|
|
||||||
END(_setjmp)
|
|
||||||
|
|
||||||
|
|
||||||
LEAF(_longjmp, FRAMESZ)
|
|
||||||
PTR_SUBU sp, FRAMESZ
|
|
||||||
SETUP_GP64(GPOFF, _longjmp)
|
|
||||||
SAVE_GP(GPOFF)
|
|
||||||
.set reorder
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
addiu a0, 7 # roundup jmpbuf addr to 8-byte boundary
|
|
||||||
li t0, ~7
|
|
||||||
and a0, t0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
# SC_MASK is unused here
|
|
||||||
|
|
||||||
lw v0, SC_MAGIC(a0)
|
|
||||||
li t0, 0xACEDBADE
|
|
||||||
bne v0, t0, _longjmp_botch # jump if error
|
|
||||||
|
|
||||||
# callee-saved long-sized regs:
|
|
||||||
REG_L ra, SC_REGS+0*REGSZ(a0)
|
|
||||||
REG_L s0, SC_REGS+1*REGSZ(a0)
|
|
||||||
REG_L s1, SC_REGS+2*REGSZ(a0)
|
|
||||||
REG_L s2, SC_REGS+3*REGSZ(a0)
|
|
||||||
REG_L s3, SC_REGS+4*REGSZ(a0)
|
|
||||||
REG_L s4, SC_REGS+5*REGSZ(a0)
|
|
||||||
REG_L s5, SC_REGS+6*REGSZ(a0)
|
|
||||||
REG_L s6, SC_REGS+7*REGSZ(a0)
|
|
||||||
REG_L s7, SC_REGS+8*REGSZ(a0)
|
|
||||||
REG_L s8, SC_REGS+9*REGSZ(a0)
|
|
||||||
REG_L gp, SC_REGS+10*REGSZ(a0)
|
|
||||||
REG_L sp, SC_REGS+11*REGSZ(a0)
|
|
||||||
|
|
||||||
lw v0, SC_FPSR(a0)
|
|
||||||
ctc1 v0, $31
|
|
||||||
#ifdef __LP64__
|
|
||||||
# callee-saved fp regs on mips n64 ABI are $f24..$f31
|
|
||||||
l.d $f24, SC_FPREGS+0*REGSZ_FP(a0)
|
|
||||||
l.d $f25, SC_FPREGS+1*REGSZ_FP(a0)
|
|
||||||
l.d $f26, SC_FPREGS+2*REGSZ_FP(a0)
|
|
||||||
l.d $f27, SC_FPREGS+3*REGSZ_FP(a0)
|
|
||||||
l.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
|
|
||||||
l.d $f29, SC_FPREGS+5*REGSZ_FP(a0)
|
|
||||||
l.d $f30, SC_FPREGS+6*REGSZ_FP(a0)
|
|
||||||
l.d $f31, SC_FPREGS+7*REGSZ_FP(a0)
|
|
||||||
#else
|
|
||||||
# callee-saved fp regs on mips o32 ABI are
|
|
||||||
# the even-numbered fp regs $f20,$f22,...$f30
|
|
||||||
l.d $f20, SC_FPREGS+0*REGSZ_FP(a0)
|
|
||||||
l.d $f22, SC_FPREGS+1*REGSZ_FP(a0)
|
|
||||||
l.d $f24, SC_FPREGS+2*REGSZ_FP(a0)
|
|
||||||
l.d $f26, SC_FPREGS+3*REGSZ_FP(a0)
|
|
||||||
l.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
|
|
||||||
l.d $f30, SC_FPREGS+5*REGSZ_FP(a0)
|
|
||||||
#endif
|
|
||||||
bne a1, zero, 1f
|
|
||||||
li a1, 1 # never return 0!
|
|
||||||
1:
|
|
||||||
move v0, a1
|
|
||||||
j ra
|
|
||||||
|
|
||||||
_longjmp_botch:
|
|
||||||
jal longjmperror
|
|
||||||
jal abort
|
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
END(_longjmp)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* trampolines for sigsetjmp and siglongjmp save and restore mask.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
FRAMESZ= MKFSIZ(1,1)
|
|
||||||
GPOFF= FRAMESZ-2*REGSZ
|
|
||||||
|
|
||||||
LEAF(sigsetjmp, FRAMESZ)
|
|
||||||
PTR_SUBU sp, FRAMESZ
|
|
||||||
SETUP_GP64(GPOFF, sigsetjmp)
|
|
||||||
.set reorder
|
|
||||||
sw a1, _JBLEN*REGSZ(a0) # save "savemask"
|
|
||||||
bne a1, 0x0, 1f # do saving of signal mask?
|
|
||||||
LA t9, _setjmp
|
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
jr t9
|
|
||||||
|
|
||||||
1: LA t9, setjmp
|
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
jr t9
|
|
||||||
END(sigsetjmp)
|
|
||||||
|
|
||||||
LEAF(siglongjmp, FRAMESZ)
|
|
||||||
PTR_SUBU sp, FRAMESZ
|
|
||||||
SETUP_GP64(GPOFF, siglongjmp)
|
|
||||||
.set reorder
|
|
||||||
lw t0, _JBLEN*REGSZ(a0) # get "savemask"
|
|
||||||
bne t0, 0x0, 1f # restore signal mask?
|
|
||||||
LA t9, _longjmp
|
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
jr t9
|
|
||||||
1:
|
|
||||||
LA t9, longjmp
|
|
||||||
RESTORE_GP64
|
|
||||||
PTR_ADDU sp, FRAMESZ
|
|
||||||
jr t9
|
|
||||||
END(siglongjmp)
|
END(siglongjmp)
|
||||||
|
|
||||||
|
|
||||||
|
.globl longjmp
|
||||||
|
.type longjmp, @function
|
||||||
|
.equ longjmp, siglongjmp # alias for siglongjmp
|
||||||
|
|
||||||
|
|
||||||
|
.globl _longjmp
|
||||||
|
.type _longjmp, @function
|
||||||
|
.equ _longjmp, siglongjmp # alias for siglongjmp
|
||||||
|
@ -6,9 +6,10 @@
|
|||||||
#define _MIPS_SETJMP_H_
|
#define _MIPS_SETJMP_H_
|
||||||
|
|
||||||
#ifdef __LP64__
|
#ifdef __LP64__
|
||||||
#define _JBLEN 22 /* size, in 8-byte longs, of a mips64 jmp_buf */
|
#define _JBLEN 25 /* size, in 8-byte longs, of a mips64 jmp_buf/sigjmp_buf */
|
||||||
#else
|
#else
|
||||||
#define _JBLEN 29 /* size, in 4-byte longs, of a mips32 jmp_buf */
|
#define _JBLEN 157 /* historical size, in 4-byte longs, of a mips32 jmp_buf */
|
||||||
|
/* actual used size is 34 */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* !_MIPS_SETJMP_H_ */
|
#endif /* !_MIPS_SETJMP_H_ */
|
||||||
|
@ -10,8 +10,6 @@ libc_bionic_src_files_mips += \
|
|||||||
bionic/__memset_chk.cpp \
|
bionic/__memset_chk.cpp \
|
||||||
bionic/__strcpy_chk.cpp \
|
bionic/__strcpy_chk.cpp \
|
||||||
bionic/__strcat_chk.cpp \
|
bionic/__strcat_chk.cpp \
|
||||||
bionic/memchr.c \
|
|
||||||
bionic/memrchr.c \
|
|
||||||
bionic/strchr.cpp \
|
bionic/strchr.cpp \
|
||||||
bionic/strnlen.c \
|
bionic/strnlen.c \
|
||||||
bionic/strrchr.cpp \
|
bionic/strrchr.cpp \
|
||||||
@ -27,7 +25,9 @@ libc_freebsd_src_files_mips += \
|
|||||||
upstream-freebsd/lib/libc/string/wmemmove.c \
|
upstream-freebsd/lib/libc/string/wmemmove.c \
|
||||||
|
|
||||||
libc_openbsd_src_files_mips += \
|
libc_openbsd_src_files_mips += \
|
||||||
|
upstream-openbsd/lib/libc/string/memchr.c \
|
||||||
upstream-openbsd/lib/libc/string/memmove.c \
|
upstream-openbsd/lib/libc/string/memmove.c \
|
||||||
|
upstream-openbsd/lib/libc/string/memrchr.c \
|
||||||
upstream-openbsd/lib/libc/string/stpcpy.c \
|
upstream-openbsd/lib/libc/string/stpcpy.c \
|
||||||
upstream-openbsd/lib/libc/string/stpncpy.c \
|
upstream-openbsd/lib/libc/string/stpncpy.c \
|
||||||
upstream-openbsd/lib/libc/string/strcat.c \
|
upstream-openbsd/lib/libc/string/strcat.c \
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(faccessat)
|
ENTRY(___faccessat)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
.cpload t9
|
.cpload t9
|
||||||
li v0, __NR_faccessat
|
li v0, __NR_faccessat
|
||||||
@ -16,4 +16,5 @@ ENTRY(faccessat)
|
|||||||
j t9
|
j t9
|
||||||
nop
|
nop
|
||||||
.set reorder
|
.set reorder
|
||||||
END(faccessat)
|
END(___faccessat)
|
||||||
|
.hidden ___faccessat
|
@ -29,7 +29,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <asm/unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
struct kernel_stat {
|
struct kernel_stat {
|
||||||
unsigned int st_dev;
|
unsigned int st_dev;
|
||||||
|
@ -9,8 +9,6 @@ libc_bionic_src_files_mips64 += \
|
|||||||
bionic/__memset_chk.cpp \
|
bionic/__memset_chk.cpp \
|
||||||
bionic/__strcpy_chk.cpp \
|
bionic/__strcpy_chk.cpp \
|
||||||
bionic/__strcat_chk.cpp \
|
bionic/__strcat_chk.cpp \
|
||||||
bionic/memchr.c \
|
|
||||||
bionic/memrchr.c \
|
|
||||||
bionic/strchr.cpp \
|
bionic/strchr.cpp \
|
||||||
bionic/strnlen.c \
|
bionic/strnlen.c \
|
||||||
bionic/strrchr.cpp \
|
bionic/strrchr.cpp \
|
||||||
@ -30,7 +28,9 @@ libc_freebsd_src_files_mips64 += \
|
|||||||
upstream-freebsd/lib/libc/string/wmemmove.c \
|
upstream-freebsd/lib/libc/string/wmemmove.c \
|
||||||
|
|
||||||
libc_openbsd_src_files_mips64 += \
|
libc_openbsd_src_files_mips64 += \
|
||||||
|
upstream-openbsd/lib/libc/string/memchr.c \
|
||||||
upstream-openbsd/lib/libc/string/memmove.c \
|
upstream-openbsd/lib/libc/string/memmove.c \
|
||||||
|
upstream-openbsd/lib/libc/string/memrchr.c \
|
||||||
upstream-openbsd/lib/libc/string/stpcpy.c \
|
upstream-openbsd/lib/libc/string/stpcpy.c \
|
||||||
upstream-openbsd/lib/libc/string/stpncpy.c \
|
upstream-openbsd/lib/libc/string/stpncpy.c \
|
||||||
upstream-openbsd/lib/libc/string/strcat.c \
|
upstream-openbsd/lib/libc/string/strcat.c \
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(faccessat)
|
ENTRY(___faccessat)
|
||||||
.set push
|
.set push
|
||||||
.set noreorder
|
.set noreorder
|
||||||
li v0, __NR_faccessat
|
li v0, __NR_faccessat
|
||||||
@ -22,4 +22,5 @@ ENTRY(faccessat)
|
|||||||
j t9
|
j t9
|
||||||
move ra, t0
|
move ra, t0
|
||||||
.set pop
|
.set pop
|
||||||
END(faccessat)
|
END(___faccessat)
|
||||||
|
.hidden ___faccessat
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(faccessat)
|
ENTRY(___faccessat)
|
||||||
pushl %ebx
|
pushl %ebx
|
||||||
.cfi_def_cfa_offset 8
|
.cfi_def_cfa_offset 8
|
||||||
.cfi_rel_offset ebx, 0
|
.cfi_rel_offset ebx, 0
|
||||||
@ -12,13 +12,9 @@ ENTRY(faccessat)
|
|||||||
pushl %edx
|
pushl %edx
|
||||||
.cfi_adjust_cfa_offset 4
|
.cfi_adjust_cfa_offset 4
|
||||||
.cfi_rel_offset edx, 0
|
.cfi_rel_offset edx, 0
|
||||||
pushl %esi
|
mov 16(%esp), %ebx
|
||||||
.cfi_adjust_cfa_offset 4
|
mov 20(%esp), %ecx
|
||||||
.cfi_rel_offset esi, 0
|
mov 24(%esp), %edx
|
||||||
mov 20(%esp), %ebx
|
|
||||||
mov 24(%esp), %ecx
|
|
||||||
mov 28(%esp), %edx
|
|
||||||
mov 32(%esp), %esi
|
|
||||||
movl $__NR_faccessat, %eax
|
movl $__NR_faccessat, %eax
|
||||||
int $0x80
|
int $0x80
|
||||||
cmpl $-MAX_ERRNO, %eax
|
cmpl $-MAX_ERRNO, %eax
|
||||||
@ -28,9 +24,9 @@ ENTRY(faccessat)
|
|||||||
call __set_errno_internal
|
call __set_errno_internal
|
||||||
addl $4, %esp
|
addl $4, %esp
|
||||||
1:
|
1:
|
||||||
popl %esi
|
|
||||||
popl %edx
|
popl %edx
|
||||||
popl %ecx
|
popl %ecx
|
||||||
popl %ebx
|
popl %ebx
|
||||||
ret
|
ret
|
||||||
END(faccessat)
|
END(___faccessat)
|
||||||
|
.hidden ___faccessat
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(faccessat)
|
ENTRY(___faccessat)
|
||||||
movq %rcx, %r10
|
|
||||||
movl $__NR_faccessat, %eax
|
movl $__NR_faccessat, %eax
|
||||||
syscall
|
syscall
|
||||||
cmpq $-MAX_ERRNO, %rax
|
cmpq $-MAX_ERRNO, %rax
|
||||||
@ -13,4 +12,5 @@ ENTRY(faccessat)
|
|||||||
call __set_errno_internal
|
call __set_errno_internal
|
||||||
1:
|
1:
|
||||||
ret
|
ret
|
||||||
END(faccessat)
|
END(___faccessat)
|
||||||
|
.hidden ___faccessat
|
@ -9,8 +9,6 @@ libc_bionic_src_files_x86_64 += \
|
|||||||
bionic/__memset_chk.cpp \
|
bionic/__memset_chk.cpp \
|
||||||
bionic/__strcpy_chk.cpp \
|
bionic/__strcpy_chk.cpp \
|
||||||
bionic/__strcat_chk.cpp \
|
bionic/__strcat_chk.cpp \
|
||||||
bionic/memchr.c \
|
|
||||||
bionic/memrchr.c \
|
|
||||||
bionic/strchr.cpp \
|
bionic/strchr.cpp \
|
||||||
bionic/strnlen.c \
|
bionic/strnlen.c \
|
||||||
bionic/strrchr.cpp \
|
bionic/strrchr.cpp \
|
||||||
@ -25,6 +23,10 @@ libc_freebsd_src_files_x86_64 += \
|
|||||||
upstream-freebsd/lib/libc/string/wmemcmp.c \
|
upstream-freebsd/lib/libc/string/wmemcmp.c \
|
||||||
upstream-freebsd/lib/libc/string/wmemmove.c \
|
upstream-freebsd/lib/libc/string/wmemmove.c \
|
||||||
|
|
||||||
|
libc_openbsd_src_files_x86_64 += \
|
||||||
|
upstream-openbsd/lib/libc/string/memchr.c \
|
||||||
|
upstream-openbsd/lib/libc/string/memrchr.c \
|
||||||
|
|
||||||
#
|
#
|
||||||
# Inherently architecture-specific code.
|
# Inherently architecture-specific code.
|
||||||
#
|
#
|
||||||
|
@ -26,8 +26,19 @@
|
|||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
int dup2(int old_fd, int new_fd) {
|
int dup2(int old_fd, int new_fd) {
|
||||||
|
// If old_fd is equal to new_fd and a valid file descriptor, dup2 returns
|
||||||
|
// old_fd without closing it. This is not true of dup3, so we have to
|
||||||
|
// handle this case ourselves.
|
||||||
|
if (old_fd == new_fd) {
|
||||||
|
if (fcntl(old_fd, F_GETFD) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return old_fd;
|
||||||
|
}
|
||||||
|
|
||||||
return dup3(old_fd, new_fd, 0);
|
return dup3(old_fd, new_fd, 0);
|
||||||
}
|
}
|
||||||
|
59
libc/bionic/faccessat.cpp
Normal file
59
libc/bionic/faccessat.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
extern "C" int ___faccessat(int, const char*, int);
|
||||||
|
|
||||||
|
int faccessat(int dirfd, const char* pathname, int mode, int flags) {
|
||||||
|
// "The mode specifies the accessibility check(s) to be performed,
|
||||||
|
// and is either the value F_OK, or a mask consisting of the
|
||||||
|
// bitwise OR of one or more of R_OK, W_OK, and X_OK."
|
||||||
|
if ((mode != F_OK) && ((mode & ~(R_OK | W_OK | X_OK)) != 0) &&
|
||||||
|
((mode & (R_OK | W_OK | X_OK)) == 0)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags != 0) {
|
||||||
|
// We deliberately don't support AT_SYMLINK_NOFOLLOW, a glibc
|
||||||
|
// only feature which is error prone and dangerous.
|
||||||
|
//
|
||||||
|
// AT_EACCESS isn't supported either. Android doesn't have setuid
|
||||||
|
// programs, and never runs code with euid!=uid. It could be
|
||||||
|
// implemented in an expensive way, following the model at
|
||||||
|
// https://gitlab.com/bminor/musl/commit/0a05eace163cee9b08571d2ff9d90f5e82d9c228
|
||||||
|
// but not worth it.
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ___faccessat(dirfd, pathname, mode);
|
||||||
|
}
|
@ -88,7 +88,6 @@ void __libc_init_tls(KernelArgumentBlock& args) {
|
|||||||
// The main thread has no mmap allocated space for stack or pthread_internal_t.
|
// The main thread has no mmap allocated space for stack or pthread_internal_t.
|
||||||
main_thread.mmap_size = 0;
|
main_thread.mmap_size = 0;
|
||||||
pthread_attr_init(&main_thread.attr);
|
pthread_attr_init(&main_thread.attr);
|
||||||
main_thread.attr.flags = PTHREAD_ATTR_FLAG_MAIN_THREAD;
|
|
||||||
main_thread.attr.guard_size = 0; // The main thread has no guard page.
|
main_thread.attr.guard_size = 0; // The main thread has no guard page.
|
||||||
main_thread.attr.stack_size = 0; // User code should never see this; we'll compute it when asked.
|
main_thread.attr.stack_size = 0; // User code should never see this; we'll compute it when asked.
|
||||||
// TODO: the main thread's sched_policy and sched_priority need to be queried.
|
// TODO: the main thread's sched_policy and sched_priority need to be queried.
|
||||||
|
@ -58,15 +58,18 @@ struct __locale_t {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(__locale_t);
|
DISALLOW_COPY_AND_ASSIGN(__locale_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t __ctype_get_mb_cur_max() {
|
||||||
|
locale_t l = uselocale(NULL);
|
||||||
|
if (l == LC_GLOBAL_LOCALE) {
|
||||||
|
return __bionic_current_locale_is_utf8 ? 4 : 1;
|
||||||
|
} else {
|
||||||
|
return l->mb_cur_max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static pthread_once_t g_locale_once = PTHREAD_ONCE_INIT;
|
static pthread_once_t g_locale_once = PTHREAD_ONCE_INIT;
|
||||||
static lconv g_locale;
|
static lconv g_locale;
|
||||||
|
|
||||||
// We don't use pthread_once for this so that we know when the resource (a TLS slot) will be taken.
|
|
||||||
static pthread_key_t g_uselocale_key;
|
|
||||||
__attribute__((constructor)) static void __bionic_tls_uselocale_key_init() {
|
|
||||||
pthread_key_create(&g_uselocale_key, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __locale_init() {
|
static void __locale_init() {
|
||||||
g_locale.decimal_point = const_cast<char*>(".");
|
g_locale.decimal_point = const_cast<char*>(".");
|
||||||
|
|
||||||
@ -97,15 +100,6 @@ static void __locale_init() {
|
|||||||
g_locale.int_n_sign_posn = CHAR_MAX;
|
g_locale.int_n_sign_posn = CHAR_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t __ctype_get_mb_cur_max() {
|
|
||||||
locale_t l = reinterpret_cast<locale_t>(pthread_getspecific(g_uselocale_key));
|
|
||||||
if (l == nullptr || l == LC_GLOBAL_LOCALE) {
|
|
||||||
return __bionic_current_locale_is_utf8 ? 4 : 1;
|
|
||||||
} else {
|
|
||||||
return l->mb_cur_max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool __is_supported_locale(const char* locale) {
|
static bool __is_supported_locale(const char* locale) {
|
||||||
return (strcmp(locale, "") == 0 ||
|
return (strcmp(locale, "") == 0 ||
|
||||||
strcmp(locale, "C") == 0 ||
|
strcmp(locale, "C") == 0 ||
|
||||||
@ -162,7 +156,16 @@ char* setlocale(int category, const char* locale_name) {
|
|||||||
return const_cast<char*>(__bionic_current_locale_is_utf8 ? "C.UTF-8" : "C");
|
return const_cast<char*>(__bionic_current_locale_is_utf8 ? "C.UTF-8" : "C");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can't use a constructor to create g_uselocal_key, because it may be used in constructors.
|
||||||
|
static pthread_once_t g_uselocale_once = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_key_t g_uselocale_key;
|
||||||
|
|
||||||
|
static void g_uselocale_key_init() {
|
||||||
|
pthread_key_create(&g_uselocale_key, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
locale_t uselocale(locale_t new_locale) {
|
locale_t uselocale(locale_t new_locale) {
|
||||||
|
pthread_once(&g_uselocale_once, g_uselocale_key_init);
|
||||||
locale_t old_locale = static_cast<locale_t>(pthread_getspecific(g_uselocale_key));
|
locale_t old_locale = static_cast<locale_t>(pthread_getspecific(g_uselocale_key));
|
||||||
|
|
||||||
// If this is the first call to uselocale(3) on this thread, we return LC_GLOBAL_LOCALE.
|
// If this is the first call to uselocale(3) on this thread, we return LC_GLOBAL_LOCALE.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -25,22 +25,9 @@
|
|||||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void *memchr(const void *s, int c, size_t n)
|
void* mempcpy(void* dst, const void* src, size_t n) {
|
||||||
{
|
return reinterpret_cast<char*>(memcpy(dst, src, n)) + n;
|
||||||
const unsigned char* p = s;
|
|
||||||
const unsigned char* end = p + n;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (p >= end || p[0] == c) break; p++;
|
|
||||||
if (p >= end || p[0] == c) break; p++;
|
|
||||||
if (p >= end || p[0] == c) break; p++;
|
|
||||||
if (p >= end || p[0] == c) break; p++;
|
|
||||||
}
|
|
||||||
if (p >= end)
|
|
||||||
return NULL;
|
|
||||||
else
|
|
||||||
return (void*) p;
|
|
||||||
}
|
}
|
@ -26,11 +26,11 @@
|
|||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This file perpetuates the mistakes of the past, but only for 32-bit targets.
|
// This file perpetuates the mistakes of the past.
|
||||||
#if !defined(__LP64__)
|
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -45,6 +45,11 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include "private/libc_logging.h"
|
||||||
|
|
||||||
|
// The part is only for 32-bit targets.
|
||||||
|
#if !defined(__LP64__)
|
||||||
|
|
||||||
// These were accidentally declared in <unistd.h> because we stupidly used to inline
|
// These were accidentally declared in <unistd.h> because we stupidly used to inline
|
||||||
// getpagesize() and __getpageshift(). Needed for backwards compatibility with old NDK apps.
|
// getpagesize() and __getpageshift(). Needed for backwards compatibility with old NDK apps.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -341,4 +346,18 @@ extern "C" void* dlmalloc(size_t size) {
|
|||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // !defined(__LP64__)
|
||||||
|
|
||||||
|
// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
|
||||||
|
extern "C" char* getusershell() {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
|
||||||
|
extern "C" void setusershell() { }
|
||||||
|
|
||||||
|
// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
|
||||||
|
extern "C" void endusershell() { }
|
||||||
|
|
||||||
|
// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
|
||||||
|
extern "C" void endpwent() { }
|
||||||
|
@ -152,9 +152,6 @@ static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_
|
|||||||
}
|
}
|
||||||
|
|
||||||
int pthread_attr_getstack(const pthread_attr_t* attr, void** stack_base, size_t* stack_size) {
|
int pthread_attr_getstack(const pthread_attr_t* attr, void** stack_base, size_t* stack_size) {
|
||||||
if ((attr->flags & PTHREAD_ATTR_FLAG_MAIN_THREAD) != 0) {
|
|
||||||
return __pthread_attr_getstack_main_thread(stack_base, stack_size);
|
|
||||||
}
|
|
||||||
*stack_base = attr->stack_base;
|
*stack_base = attr->stack_base;
|
||||||
*stack_size = attr->stack_size;
|
*stack_size = attr->stack_size;
|
||||||
return 0;
|
return 0;
|
||||||
@ -171,7 +168,13 @@ int pthread_attr_getguardsize(const pthread_attr_t* attr, size_t* guard_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int pthread_getattr_np(pthread_t t, pthread_attr_t* attr) {
|
int pthread_getattr_np(pthread_t t, pthread_attr_t* attr) {
|
||||||
*attr = reinterpret_cast<pthread_internal_t*>(t)->attr;
|
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(t);
|
||||||
|
*attr = thread->attr;
|
||||||
|
// The main thread's stack information is not stored in thread->attr, and we need to
|
||||||
|
// collect that at runtime.
|
||||||
|
if (thread->tid == getpid()) {
|
||||||
|
return __pthread_attr_getstack_main_thread(&attr->stack_base, &attr->stack_size);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,13 @@
|
|||||||
#include "private/bionic_time_conversions.h"
|
#include "private/bionic_time_conversions.h"
|
||||||
#include "private/bionic_tls.h"
|
#include "private/bionic_tls.h"
|
||||||
|
|
||||||
|
// XXX *technically* there is a race condition that could allow
|
||||||
|
// XXX a signal to be missed. If thread A is preempted in _wait()
|
||||||
|
// XXX after unlocking the mutex and before waiting, and if other
|
||||||
|
// XXX threads call signal or broadcast UINT_MAX/2 times (exactly),
|
||||||
|
// XXX before thread A is scheduled again and calls futex_wait(),
|
||||||
|
// XXX then the signal will be lost.
|
||||||
|
|
||||||
// We use one bit in pthread_condattr_t (long) values as the 'shared' flag
|
// We use one bit in pthread_condattr_t (long) values as the 'shared' flag
|
||||||
// and one bit for the clock type (CLOCK_REALTIME is ((clockid_t) 1), and
|
// and one bit for the clock type (CLOCK_REALTIME is ((clockid_t) 1), and
|
||||||
// CLOCK_MONOTONIC is ((clockid_t) 0).). The rest of the bits are a counter.
|
// CLOCK_MONOTONIC is ((clockid_t) 0).). The rest of the bits are a counter.
|
||||||
@ -57,7 +64,6 @@
|
|||||||
#define COND_GET_CLOCK(c) (((c) & COND_CLOCK_MASK) >> 1)
|
#define COND_GET_CLOCK(c) (((c) & COND_CLOCK_MASK) >> 1)
|
||||||
#define COND_SET_CLOCK(attr, c) ((attr) | (c << 1))
|
#define COND_SET_CLOCK(attr, c) ((attr) | (c << 1))
|
||||||
|
|
||||||
|
|
||||||
int pthread_condattr_init(pthread_condattr_t* attr) {
|
int pthread_condattr_init(pthread_condattr_t* attr) {
|
||||||
*attr = 0;
|
*attr = 0;
|
||||||
*attr |= PTHREAD_PROCESS_PRIVATE;
|
*attr |= PTHREAD_PROCESS_PRIVATE;
|
||||||
@ -98,47 +104,50 @@ int pthread_condattr_destroy(pthread_condattr_t* attr) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline atomic_uint* COND_TO_ATOMIC_POINTER(pthread_cond_t* cond) {
|
struct pthread_cond_internal_t {
|
||||||
static_assert(sizeof(atomic_uint) == sizeof(cond->value),
|
atomic_uint state;
|
||||||
"cond->value should actually be atomic_uint in implementation.");
|
|
||||||
|
|
||||||
// We prefer casting to atomic_uint instead of declaring cond->value to be atomic_uint directly.
|
bool process_shared() {
|
||||||
// Because using the second method pollutes pthread.h, and causes an error when compiling libcxx.
|
return COND_IS_SHARED(atomic_load_explicit(&state, memory_order_relaxed));
|
||||||
return reinterpret_cast<atomic_uint*>(&cond->value);
|
}
|
||||||
|
|
||||||
|
int get_clock() {
|
||||||
|
return COND_GET_CLOCK(atomic_load_explicit(&state, memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__LP64__)
|
||||||
|
char __reserved[44];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static pthread_cond_internal_t* __get_internal_cond(pthread_cond_t* cond_interface) {
|
||||||
|
static_assert(sizeof(pthread_cond_t) == sizeof(pthread_cond_internal_t),
|
||||||
|
"pthread_cond_t should actually be pthread_cond_internal_t in implementation.");
|
||||||
|
return reinterpret_cast<pthread_cond_internal_t*>(cond_interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX *technically* there is a race condition that could allow
|
int pthread_cond_init(pthread_cond_t* cond_interface, const pthread_condattr_t* attr) {
|
||||||
// XXX a signal to be missed. If thread A is preempted in _wait()
|
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
||||||
// XXX after unlocking the mutex and before waiting, and if other
|
|
||||||
// XXX threads call signal or broadcast UINT_MAX/2 times (exactly),
|
|
||||||
// XXX before thread A is scheduled again and calls futex_wait(),
|
|
||||||
// XXX then the signal will be lost.
|
|
||||||
|
|
||||||
int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) {
|
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
|
||||||
unsigned int init_value = 0;
|
|
||||||
|
|
||||||
|
unsigned int init_state = 0;
|
||||||
if (attr != NULL) {
|
if (attr != NULL) {
|
||||||
init_value = (*attr & COND_FLAGS_MASK);
|
init_state = (*attr & COND_FLAGS_MASK);
|
||||||
}
|
}
|
||||||
atomic_init(cond_value_ptr, init_value);
|
atomic_init(&cond->state, init_state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_cond_destroy(pthread_cond_t* cond) {
|
int pthread_cond_destroy(pthread_cond_t* cond_interface) {
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
||||||
atomic_store_explicit(cond_value_ptr, 0xdeadc04d, memory_order_relaxed);
|
atomic_store_explicit(&cond->state, 0xdeadc04d, memory_order_relaxed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is used by pthread_cond_broadcast and
|
// This function is used by pthread_cond_broadcast and
|
||||||
// pthread_cond_signal to atomically decrement the counter
|
// pthread_cond_signal to atomically decrement the counter
|
||||||
// then wake up thread_count threads.
|
// then wake up thread_count threads.
|
||||||
static int __pthread_cond_pulse(atomic_uint* cond_value_ptr, int thread_count) {
|
static int __pthread_cond_pulse(pthread_cond_internal_t* cond, int thread_count) {
|
||||||
unsigned int old_value = atomic_load_explicit(cond_value_ptr, memory_order_relaxed);
|
|
||||||
bool shared = COND_IS_SHARED(old_value);
|
|
||||||
|
|
||||||
// We don't use a release/seq_cst fence here. Because pthread_cond_wait/signal can't be
|
// We don't use a release/seq_cst fence here. Because pthread_cond_wait/signal can't be
|
||||||
// used as a method for memory synchronization by itself. It should always be used with
|
// used as a method for memory synchronization by itself. It should always be used with
|
||||||
// pthread mutexes. Note that Spurious wakeups from pthread_cond_wait/timedwait may occur,
|
// pthread mutexes. Note that Spurious wakeups from pthread_cond_wait/timedwait may occur,
|
||||||
@ -149,20 +158,18 @@ static int __pthread_cond_pulse(atomic_uint* cond_value_ptr, int thread_count) {
|
|||||||
// synchronization. And it doesn't help even if we use any fence here.
|
// synchronization. And it doesn't help even if we use any fence here.
|
||||||
|
|
||||||
// The increase of value should leave flags alone, even if the value can overflows.
|
// The increase of value should leave flags alone, even if the value can overflows.
|
||||||
atomic_fetch_add_explicit(cond_value_ptr, COND_COUNTER_STEP, memory_order_relaxed);
|
atomic_fetch_add_explicit(&cond->state, COND_COUNTER_STEP, memory_order_relaxed);
|
||||||
|
|
||||||
__futex_wake_ex(cond_value_ptr, shared, thread_count);
|
__futex_wake_ex(&cond->state, cond->process_shared(), thread_count);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__LIBC_HIDDEN__
|
static int __pthread_cond_timedwait_relative(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
|
||||||
int __pthread_cond_timedwait_relative(atomic_uint* cond_value_ptr, pthread_mutex_t* mutex,
|
const timespec* rel_timeout_or_null) {
|
||||||
const timespec* reltime) {
|
unsigned int old_state = atomic_load_explicit(&cond->state, memory_order_relaxed);
|
||||||
unsigned int old_value = atomic_load_explicit(cond_value_ptr, memory_order_relaxed);
|
|
||||||
bool shared = COND_IS_SHARED(old_value);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(mutex);
|
pthread_mutex_unlock(mutex);
|
||||||
int status = __futex_wait_ex(cond_value_ptr, shared, old_value, reltime);
|
int status = __futex_wait_ex(&cond->state, cond->process_shared(), old_state, rel_timeout_or_null);
|
||||||
pthread_mutex_lock(mutex);
|
pthread_mutex_lock(mutex);
|
||||||
|
|
||||||
if (status == -ETIMEDOUT) {
|
if (status == -ETIMEDOUT) {
|
||||||
@ -171,67 +178,68 @@ int __pthread_cond_timedwait_relative(atomic_uint* cond_value_ptr, pthread_mutex
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__LIBC_HIDDEN__
|
static int __pthread_cond_timedwait(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
|
||||||
int __pthread_cond_timedwait(atomic_uint* cond_value_ptr, pthread_mutex_t* mutex,
|
const timespec* abs_timeout_or_null, clockid_t clock) {
|
||||||
const timespec* abs_ts, clockid_t clock) {
|
|
||||||
timespec ts;
|
timespec ts;
|
||||||
timespec* tsp;
|
timespec* rel_timeout = NULL;
|
||||||
|
|
||||||
if (abs_ts != NULL) {
|
if (abs_timeout_or_null != NULL) {
|
||||||
if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) {
|
rel_timeout = &ts;
|
||||||
|
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
tsp = &ts;
|
|
||||||
} else {
|
|
||||||
tsp = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return __pthread_cond_timedwait_relative(cond_value_ptr, mutex, tsp);
|
return __pthread_cond_timedwait_relative(cond, mutex, rel_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_cond_broadcast(pthread_cond_t* cond) {
|
int pthread_cond_broadcast(pthread_cond_t* cond_interface) {
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
return __pthread_cond_pulse(__get_internal_cond(cond_interface), INT_MAX);
|
||||||
return __pthread_cond_pulse(cond_value_ptr, INT_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_cond_signal(pthread_cond_t* cond) {
|
int pthread_cond_signal(pthread_cond_t* cond_interface) {
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
return __pthread_cond_pulse(__get_internal_cond(cond_interface), 1);
|
||||||
return __pthread_cond_pulse(cond_value_ptr, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
int pthread_cond_wait(pthread_cond_t* cond_interface, pthread_mutex_t* mutex) {
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
||||||
return __pthread_cond_timedwait(cond_value_ptr, mutex, NULL,
|
return __pthread_cond_timedwait(cond, mutex, NULL, cond->get_clock());
|
||||||
COND_GET_CLOCK(atomic_load_explicit(cond_value_ptr, memory_order_relaxed)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t * mutex, const timespec *abstime) {
|
int pthread_cond_timedwait(pthread_cond_t *cond_interface, pthread_mutex_t * mutex,
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
const timespec *abstime) {
|
||||||
return __pthread_cond_timedwait(cond_value_ptr, mutex, abstime,
|
|
||||||
COND_GET_CLOCK(atomic_load_explicit(cond_value_ptr, memory_order_relaxed)));
|
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
||||||
|
return __pthread_cond_timedwait(cond, mutex, abstime, cond->get_clock());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__LP64__)
|
#if !defined(__LP64__)
|
||||||
// TODO: this exists only for backward binary compatibility on 32 bit platforms.
|
// TODO: this exists only for backward binary compatibility on 32 bit platforms.
|
||||||
extern "C" int pthread_cond_timedwait_monotonic(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime) {
|
extern "C" int pthread_cond_timedwait_monotonic(pthread_cond_t* cond_interface,
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
pthread_mutex_t* mutex,
|
||||||
return __pthread_cond_timedwait(cond_value_ptr, mutex, abstime, CLOCK_MONOTONIC);
|
const timespec* abs_timeout) {
|
||||||
|
|
||||||
|
return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, abs_timeout,
|
||||||
|
CLOCK_MONOTONIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime) {
|
extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond_interface,
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
pthread_mutex_t* mutex,
|
||||||
return __pthread_cond_timedwait(cond_value_ptr, mutex, abstime, CLOCK_MONOTONIC);
|
const timespec* abs_timeout) {
|
||||||
|
return pthread_cond_timedwait_monotonic(cond_interface, mutex, abs_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* reltime) {
|
extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond_interface,
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
pthread_mutex_t* mutex,
|
||||||
return __pthread_cond_timedwait_relative(cond_value_ptr, mutex, reltime);
|
const timespec* rel_timeout) {
|
||||||
|
|
||||||
|
return __pthread_cond_timedwait_relative(__get_internal_cond(cond_interface), mutex, rel_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond, pthread_mutex_t* mutex, unsigned ms) {
|
extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond_interface,
|
||||||
|
pthread_mutex_t* mutex, unsigned ms) {
|
||||||
timespec ts;
|
timespec ts;
|
||||||
timespec_from_ms(ts, ms);
|
timespec_from_ms(ts, ms);
|
||||||
atomic_uint* cond_value_ptr = COND_TO_ATOMIC_POINTER(cond);
|
return pthread_cond_timedwait_relative_np(cond_interface, mutex, &ts);
|
||||||
return __pthread_cond_timedwait_relative(cond_value_ptr, mutex, &ts);
|
|
||||||
}
|
}
|
||||||
#endif // !defined(__LP64__)
|
#endif // !defined(__LP64__)
|
||||||
|
@ -56,7 +56,8 @@ void __init_tls(pthread_internal_t* thread) {
|
|||||||
if (thread->mmap_size == 0) {
|
if (thread->mmap_size == 0) {
|
||||||
// If the TLS area was not allocated by mmap(), it may not have been cleared to zero.
|
// If the TLS area was not allocated by mmap(), it may not have been cleared to zero.
|
||||||
// So assume the worst and zero the TLS area.
|
// So assume the worst and zero the TLS area.
|
||||||
memset(&thread->tls[0], 0, BIONIC_TLS_SLOTS * sizeof(void*));
|
memset(thread->tls, 0, sizeof(thread->tls));
|
||||||
|
memset(thread->key_data, 0, sizeof(thread->key_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot 0 must point to itself. The x86 Linux kernel reads the TLS from %fs:0.
|
// Slot 0 must point to itself. The x86 Linux kernel reads the TLS from %fs:0.
|
||||||
@ -155,7 +156,7 @@ static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mapped space(or user allocated stack) is used for:
|
// Mapped space(or user allocated stack) is used for:
|
||||||
// thread_internal_t (including tls array)
|
// thread_internal_t
|
||||||
// thread stack (including guard page)
|
// thread stack (including guard page)
|
||||||
stack_top -= sizeof(pthread_internal_t);
|
stack_top -= sizeof(pthread_internal_t);
|
||||||
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
|
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
|
||||||
|
@ -41,8 +41,10 @@
|
|||||||
/* Did the thread exit without freeing pthread_internal_t? */
|
/* Did the thread exit without freeing pthread_internal_t? */
|
||||||
#define PTHREAD_ATTR_FLAG_ZOMBIE 0x00000004
|
#define PTHREAD_ATTR_FLAG_ZOMBIE 0x00000004
|
||||||
|
|
||||||
/* Is this the main thread? */
|
struct pthread_key_data_t {
|
||||||
#define PTHREAD_ATTR_FLAG_MAIN_THREAD 0x80000000
|
uintptr_t seq; // Use uintptr_t just for alignment, as we use pointer below.
|
||||||
|
void* data;
|
||||||
|
};
|
||||||
|
|
||||||
struct pthread_internal_t {
|
struct pthread_internal_t {
|
||||||
struct pthread_internal_t* next;
|
struct pthread_internal_t* next;
|
||||||
@ -86,6 +88,8 @@ struct pthread_internal_t {
|
|||||||
|
|
||||||
void* tls[BIONIC_TLS_SLOTS];
|
void* tls[BIONIC_TLS_SLOTS];
|
||||||
|
|
||||||
|
pthread_key_data_t key_data[BIONIC_PTHREAD_KEY_COUNT];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The dynamic linker implements dlerror(3), which makes it hard for us to implement this
|
* The dynamic linker implements dlerror(3), which makes it hard for us to implement this
|
||||||
* per-thread buffer by simply using malloc(3) and free(3).
|
* per-thread buffer by simply using malloc(3) and free(3).
|
||||||
|
@ -28,175 +28,98 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#include "private/bionic_tls.h"
|
#include "private/bionic_tls.h"
|
||||||
#include "pthread_internal.h"
|
#include "pthread_internal.h"
|
||||||
|
|
||||||
/* A technical note regarding our thread-local-storage (TLS) implementation:
|
|
||||||
*
|
|
||||||
* There can be up to BIONIC_TLS_SLOTS independent TLS keys in a given process,
|
|
||||||
* The keys below TLS_SLOT_FIRST_USER_SLOT are reserved for Bionic to hold
|
|
||||||
* special thread-specific variables like errno or a pointer to
|
|
||||||
* the current thread's descriptor. These entries cannot be accessed through
|
|
||||||
* pthread_getspecific() / pthread_setspecific() or pthread_key_delete()
|
|
||||||
*
|
|
||||||
* The 'tls_map_t' type defined below implements a shared global map of
|
|
||||||
* currently created/allocated TLS keys and the destructors associated
|
|
||||||
* with them.
|
|
||||||
*
|
|
||||||
* The global TLS map simply contains a bitmap of allocated keys, and
|
|
||||||
* an array of destructors.
|
|
||||||
*
|
|
||||||
* Each thread has a TLS area that is a simple array of BIONIC_TLS_SLOTS void*
|
|
||||||
* pointers. the TLS area of the main thread is stack-allocated in
|
|
||||||
* __libc_init_common, while the TLS area of other threads is placed at
|
|
||||||
* the top of their stack in pthread_create.
|
|
||||||
*
|
|
||||||
* When pthread_key_delete() is called it will erase the key's bitmap bit
|
|
||||||
* and its destructor, and will also clear the key data in the TLS area of
|
|
||||||
* all created threads. As mandated by Posix, it is the responsibility of
|
|
||||||
* the caller of pthread_key_delete() to properly reclaim the objects that
|
|
||||||
* were pointed to by these data fields (either before or after the call).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define TLSMAP_BITS 32
|
|
||||||
#define TLSMAP_WORDS ((BIONIC_TLS_SLOTS+TLSMAP_BITS-1)/TLSMAP_BITS)
|
|
||||||
#define TLSMAP_WORD(m,k) (m).map[(k)/TLSMAP_BITS]
|
|
||||||
#define TLSMAP_MASK(k) (1U << ((k)&(TLSMAP_BITS-1)))
|
|
||||||
|
|
||||||
static inline bool IsValidUserKey(pthread_key_t key) {
|
|
||||||
return (key >= TLS_SLOT_FIRST_USER_SLOT && key < BIONIC_TLS_SLOTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void (*key_destructor_t)(void*);
|
typedef void (*key_destructor_t)(void*);
|
||||||
|
|
||||||
struct tls_map_t {
|
#define SEQ_KEY_IN_USE_BIT 0
|
||||||
bool is_initialized;
|
|
||||||
|
|
||||||
/* bitmap of allocated keys */
|
#define SEQ_INCREMENT_STEP (1 << SEQ_KEY_IN_USE_BIT)
|
||||||
uint32_t map[TLSMAP_WORDS];
|
|
||||||
|
|
||||||
key_destructor_t key_destructors[BIONIC_TLS_SLOTS];
|
// pthread_key_internal_t records the use of each pthread key slot:
|
||||||
|
// seq records the state of the slot.
|
||||||
|
// bit 0 is 1 when the key is in use, 0 when it is unused. Each time we create or delete the
|
||||||
|
// pthread key in the slot, we increse the seq by 1 (which inverts bit 0). The reason to use
|
||||||
|
// a sequence number instead of a boolean value here is that when the key slot is deleted and
|
||||||
|
// reused for a new key, pthread_getspecific will not return stale data.
|
||||||
|
// key_destructor records the destructor called at thread exit.
|
||||||
|
struct pthread_key_internal_t {
|
||||||
|
atomic_uintptr_t seq;
|
||||||
|
atomic_uintptr_t key_destructor;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScopedTlsMapAccess {
|
static pthread_key_internal_t key_map[BIONIC_PTHREAD_KEY_COUNT];
|
||||||
public:
|
|
||||||
ScopedTlsMapAccess() {
|
|
||||||
Lock();
|
|
||||||
|
|
||||||
// If this is the first time the TLS map has been accessed,
|
static inline bool SeqOfKeyInUse(uintptr_t seq) {
|
||||||
// mark the slots belonging to well-known keys as being in use.
|
return seq & (1 << SEQ_KEY_IN_USE_BIT);
|
||||||
// This isn't currently necessary because the well-known keys
|
}
|
||||||
// can only be accessed directly by bionic itself, do not have
|
|
||||||
// destructors, and all the functions that touch the TLS map
|
|
||||||
// start after the maximum well-known slot.
|
|
||||||
if (!s_tls_map_.is_initialized) {
|
|
||||||
for (pthread_key_t key = 0; key < TLS_SLOT_FIRST_USER_SLOT; ++key) {
|
|
||||||
SetInUse(key, NULL);
|
|
||||||
}
|
|
||||||
s_tls_map_.is_initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~ScopedTlsMapAccess() {
|
static inline bool KeyInValidRange(pthread_key_t key) {
|
||||||
Unlock();
|
return key >= 0 && key < BIONIC_PTHREAD_KEY_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CreateKey(pthread_key_t* result, void (*key_destructor)(void*)) {
|
|
||||||
// Take the first unallocated key.
|
|
||||||
for (int key = 0; key < BIONIC_TLS_SLOTS; ++key) {
|
|
||||||
if (!IsInUse(key)) {
|
|
||||||
SetInUse(key, key_destructor);
|
|
||||||
*result = key;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We hit PTHREAD_KEYS_MAX. POSIX says EAGAIN for this case.
|
|
||||||
return EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeleteKey(pthread_key_t key) {
|
|
||||||
TLSMAP_WORD(s_tls_map_, key) &= ~TLSMAP_MASK(key);
|
|
||||||
s_tls_map_.key_destructors[key] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInUse(pthread_key_t key) {
|
|
||||||
return (TLSMAP_WORD(s_tls_map_, key) & TLSMAP_MASK(key)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetInUse(pthread_key_t key, void (*key_destructor)(void*)) {
|
|
||||||
TLSMAP_WORD(s_tls_map_, key) |= TLSMAP_MASK(key);
|
|
||||||
s_tls_map_.key_destructors[key] = key_destructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called from pthread_exit() to remove all TLS key data
|
|
||||||
// from this thread's TLS area. This must call the destructor of all keys
|
|
||||||
// that have a non-NULL data value and a non-NULL destructor.
|
|
||||||
void CleanAll() {
|
|
||||||
void** tls = __get_tls();
|
|
||||||
|
|
||||||
// Because destructors can do funky things like deleting/creating other
|
|
||||||
// keys, we need to implement this in a loop.
|
|
||||||
for (int rounds = PTHREAD_DESTRUCTOR_ITERATIONS; rounds > 0; --rounds) {
|
|
||||||
size_t called_destructor_count = 0;
|
|
||||||
for (int key = 0; key < BIONIC_TLS_SLOTS; ++key) {
|
|
||||||
if (IsInUse(key)) {
|
|
||||||
void* data = tls[key];
|
|
||||||
void (*key_destructor)(void*) = s_tls_map_.key_destructors[key];
|
|
||||||
|
|
||||||
if (data != NULL && key_destructor != NULL) {
|
|
||||||
// we need to clear the key data now, this will prevent the
|
|
||||||
// destructor (or a later one) from seeing the old value if
|
|
||||||
// it calls pthread_getspecific() for some odd reason
|
|
||||||
|
|
||||||
// we do not do this if 'key_destructor == NULL' just in case another
|
|
||||||
// destructor function might be responsible for manually
|
|
||||||
// releasing the corresponding data.
|
|
||||||
tls[key] = NULL;
|
|
||||||
|
|
||||||
// because the destructor is free to call pthread_key_create
|
|
||||||
// and/or pthread_key_delete, we need to temporarily unlock
|
|
||||||
// the TLS map
|
|
||||||
Unlock();
|
|
||||||
(*key_destructor)(data);
|
|
||||||
Lock();
|
|
||||||
++called_destructor_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't call any destructors, there is no need to check the TLS data again.
|
|
||||||
if (called_destructor_count == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static tls_map_t s_tls_map_;
|
|
||||||
static pthread_mutex_t s_tls_map_lock_;
|
|
||||||
|
|
||||||
void Lock() {
|
|
||||||
pthread_mutex_lock(&s_tls_map_lock_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unlock() {
|
|
||||||
pthread_mutex_unlock(&s_tls_map_lock_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
__LIBC_HIDDEN__ tls_map_t ScopedTlsMapAccess::s_tls_map_;
|
|
||||||
__LIBC_HIDDEN__ pthread_mutex_t ScopedTlsMapAccess::s_tls_map_lock_;
|
|
||||||
|
|
||||||
|
// Called from pthread_exit() to remove all pthread keys. This must call the destructor of
|
||||||
|
// all keys that have a non-NULL data value and a non-NULL destructor.
|
||||||
__LIBC_HIDDEN__ void pthread_key_clean_all() {
|
__LIBC_HIDDEN__ void pthread_key_clean_all() {
|
||||||
ScopedTlsMapAccess tls_map;
|
// Because destructors can do funky things like deleting/creating other keys,
|
||||||
tls_map.CleanAll();
|
// we need to implement this in a loop.
|
||||||
|
pthread_key_data_t* key_data = __get_thread()->key_data;
|
||||||
|
for (size_t rounds = PTHREAD_DESTRUCTOR_ITERATIONS; rounds > 0; --rounds) {
|
||||||
|
size_t called_destructor_count = 0;
|
||||||
|
for (size_t i = 0; i < BIONIC_PTHREAD_KEY_COUNT; ++i) {
|
||||||
|
uintptr_t seq = atomic_load_explicit(&key_map[i].seq, memory_order_relaxed);
|
||||||
|
if (SeqOfKeyInUse(seq) && seq == key_data[i].seq && key_data[i].data != NULL) {
|
||||||
|
// Other threads may be calling pthread_key_delete/pthread_key_create while current thread
|
||||||
|
// is exiting. So we need to ensure we read the right key_destructor.
|
||||||
|
// We can rely on a user-established happens-before relationship between the creation and
|
||||||
|
// use of pthread key to ensure that we're not getting an earlier key_destructor.
|
||||||
|
// To avoid using the key_destructor of the newly created key in the same slot, we need to
|
||||||
|
// recheck the sequence number after reading key_destructor. As a result, we either see the
|
||||||
|
// right key_destructor, or the sequence number must have changed when we reread it below.
|
||||||
|
key_destructor_t key_destructor = reinterpret_cast<key_destructor_t>(
|
||||||
|
atomic_load_explicit(&key_map[i].key_destructor, memory_order_relaxed));
|
||||||
|
if (key_destructor == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
atomic_thread_fence(memory_order_acquire);
|
||||||
|
if (atomic_load_explicit(&key_map[i].seq, memory_order_relaxed) != seq) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to clear the key data now, this will prevent the destructor (or a later one)
|
||||||
|
// from seeing the old value if it calls pthread_getspecific().
|
||||||
|
// We don't do this if 'key_destructor == NULL' just in case another destructor
|
||||||
|
// function is responsible for manually releasing the corresponding data.
|
||||||
|
void* data = key_data[i].data;
|
||||||
|
key_data[i].data = NULL;
|
||||||
|
|
||||||
|
(*key_destructor)(data);
|
||||||
|
++called_destructor_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't call any destructors, there is no need to check the pthread keys again.
|
||||||
|
if (called_destructor_count == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
|
int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
|
||||||
ScopedTlsMapAccess tls_map;
|
for (size_t i = 0; i < BIONIC_PTHREAD_KEY_COUNT; ++i) {
|
||||||
return tls_map.CreateKey(key, key_destructor);
|
uintptr_t seq = atomic_load_explicit(&key_map[i].seq, memory_order_relaxed);
|
||||||
|
while (!SeqOfKeyInUse(seq)) {
|
||||||
|
if (atomic_compare_exchange_weak(&key_map[i].seq, &seq, seq + SEQ_INCREMENT_STEP)) {
|
||||||
|
atomic_store(&key_map[i].key_destructor, reinterpret_cast<uintptr_t>(key_destructor));
|
||||||
|
*key = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes a pthread_key_t. note that the standard mandates that this does
|
// Deletes a pthread_key_t. note that the standard mandates that this does
|
||||||
@ -204,42 +127,44 @@ int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
|
|||||||
// responsibility of the caller to properly dispose of the corresponding data
|
// responsibility of the caller to properly dispose of the corresponding data
|
||||||
// and resources, using any means it finds suitable.
|
// and resources, using any means it finds suitable.
|
||||||
int pthread_key_delete(pthread_key_t key) {
|
int pthread_key_delete(pthread_key_t key) {
|
||||||
ScopedTlsMapAccess tls_map;
|
if (!KeyInValidRange(key)) {
|
||||||
|
|
||||||
if (!IsValidUserKey(key) || !tls_map.IsInUse(key)) {
|
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
// Increase seq to invalidate values in all threads.
|
||||||
// Clear value in all threads.
|
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||||
pthread_mutex_lock(&g_thread_list_lock);
|
if (SeqOfKeyInUse(seq)) {
|
||||||
for (pthread_internal_t* t = g_thread_list; t != NULL; t = t->next) {
|
if (atomic_compare_exchange_strong(&key_map[key].seq, &seq, seq + SEQ_INCREMENT_STEP)) {
|
||||||
t->tls[key] = NULL;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tls_map.DeleteKey(key);
|
return EINVAL;
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_thread_list_lock);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* pthread_getspecific(pthread_key_t key) {
|
void* pthread_getspecific(pthread_key_t key) {
|
||||||
if (!IsValidUserKey(key)) {
|
if (!KeyInValidRange(key)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||||
// For performance reasons, we do not lock/unlock the global TLS map
|
pthread_key_data_t* data = &(__get_thread()->key_data[key]);
|
||||||
// to check that the key is properly allocated. If the key was not
|
// It is user's responsibility to synchornize between the creation and use of pthread keys,
|
||||||
// allocated, the value read from the TLS should always be NULL
|
// so we use memory_order_relaxed when checking the sequence number.
|
||||||
// due to pthread_key_delete() clearing the values for all threads.
|
if (__predict_true(SeqOfKeyInUse(seq) && data->seq == seq)) {
|
||||||
return __get_tls()[key];
|
return data->data;
|
||||||
|
}
|
||||||
|
data->data = NULL;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_setspecific(pthread_key_t key, const void* ptr) {
|
int pthread_setspecific(pthread_key_t key, const void* ptr) {
|
||||||
ScopedTlsMapAccess tls_map;
|
if (!KeyInValidRange(key)) {
|
||||||
|
|
||||||
if (!IsValidUserKey(key) || !tls_map.IsInUse(key)) {
|
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||||
__get_tls()[key] = const_cast<void*>(ptr);
|
if (SeqOfKeyInUse(seq)) {
|
||||||
return 0;
|
pthread_key_data_t* data = &(__get_thread()->key_data[key]);
|
||||||
|
data->seq = seq;
|
||||||
|
data->data = const_cast<void*>(ptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#include "pthread_internal.h"
|
#include "pthread_internal.h"
|
||||||
#include "private/bionic_futex.h"
|
#include "private/bionic_futex.h"
|
||||||
@ -52,11 +53,6 @@
|
|||||||
* - This implementation will return EDEADLK in "write after write" and "read after
|
* - This implementation will return EDEADLK in "write after write" and "read after
|
||||||
* write" cases and will deadlock in write after read case.
|
* write" cases and will deadlock in write after read case.
|
||||||
*
|
*
|
||||||
* TODO: VERY CAREFULLY convert this to use C++11 atomics when possible. All volatile
|
|
||||||
* members of pthread_rwlock_t should be converted to atomics<> and __sync_bool_compare_and_swap
|
|
||||||
* should be changed to compare_exchange_strong accompanied by the proper ordering
|
|
||||||
* constraints (comments have been added with the intending ordering across the code).
|
|
||||||
*
|
|
||||||
* TODO: As it stands now, pending_readers and pending_writers could be merged into a
|
* TODO: As it stands now, pending_readers and pending_writers could be merged into a
|
||||||
* a single waiters variable. Keeping them separate adds a bit of clarity and keeps
|
* a single waiters variable. Keeping them separate adds a bit of clarity and keeps
|
||||||
* the door open for a writer-biased implementation.
|
* the door open for a writer-biased implementation.
|
||||||
@ -66,18 +62,6 @@
|
|||||||
#define RWLOCKATTR_DEFAULT 0
|
#define RWLOCKATTR_DEFAULT 0
|
||||||
#define RWLOCKATTR_SHARED_MASK 0x0010
|
#define RWLOCKATTR_SHARED_MASK 0x0010
|
||||||
|
|
||||||
static inline bool rwlock_is_shared(const pthread_rwlock_t* rwlock) {
|
|
||||||
return rwlock->attr == PTHREAD_PROCESS_SHARED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool timespec_from_absolute(timespec* rel_timeout, const timespec* abs_timeout) {
|
|
||||||
if (abs_timeout != NULL) {
|
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout, CLOCK_REALTIME)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pthread_rwlockattr_init(pthread_rwlockattr_t* attr) {
|
int pthread_rwlockattr_init(pthread_rwlockattr_t* attr) {
|
||||||
*attr = PTHREAD_PROCESS_PRIVATE;
|
*attr = PTHREAD_PROCESS_PRIVATE;
|
||||||
@ -105,8 +89,36 @@ int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr) {
|
struct pthread_rwlock_internal_t {
|
||||||
if (attr != NULL) {
|
atomic_int state; // 0=unlock, -1=writer lock, +n=reader lock
|
||||||
|
atomic_int writer_thread_id;
|
||||||
|
atomic_uint pending_readers;
|
||||||
|
atomic_uint pending_writers;
|
||||||
|
int32_t attr;
|
||||||
|
|
||||||
|
bool process_shared() const {
|
||||||
|
return attr == PTHREAD_PROCESS_SHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__LP64__)
|
||||||
|
char __reserved[36];
|
||||||
|
#else
|
||||||
|
char __reserved[20];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline pthread_rwlock_internal_t* __get_internal_rwlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
|
static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t),
|
||||||
|
"pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation.");
|
||||||
|
return reinterpret_cast<pthread_rwlock_internal_t*>(rwlock_interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_rwlock_init(pthread_rwlock_t* rwlock_interface, const pthread_rwlockattr_t* attr) {
|
||||||
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
|
if (__predict_true(attr == NULL)) {
|
||||||
|
rwlock->attr = 0;
|
||||||
|
} else {
|
||||||
switch (*attr) {
|
switch (*attr) {
|
||||||
case PTHREAD_PROCESS_SHARED:
|
case PTHREAD_PROCESS_SHARED:
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
@ -117,165 +129,214 @@ int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* at
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rwlock->state = 0;
|
atomic_init(&rwlock->state, 0);
|
||||||
rwlock->pending_readers = 0;
|
atomic_init(&rwlock->writer_thread_id, 0);
|
||||||
rwlock->pending_writers = 0;
|
atomic_init(&rwlock->pending_readers, 0);
|
||||||
rwlock->writer_thread_id = 0;
|
atomic_init(&rwlock->pending_writers, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) {
|
int pthread_rwlock_destroy(pthread_rwlock_t* rwlock_interface) {
|
||||||
if (rwlock->state != 0) {
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
|
if (atomic_load_explicit(&rwlock->state, memory_order_relaxed) != 0) {
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock, const timespec* abs_timeout) {
|
static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
||||||
if (__predict_false(__get_thread()->tid == rwlock->writer_thread_id)) {
|
const timespec* abs_timeout_or_null) {
|
||||||
|
|
||||||
|
if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id,
|
||||||
|
memory_order_relaxed))) {
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec ts;
|
while (true) {
|
||||||
timespec* rel_timeout = (abs_timeout == NULL) ? NULL : &ts;
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
bool done = false;
|
if (__predict_true(old_state >= 0)) {
|
||||||
do {
|
if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, old_state + 1,
|
||||||
// This is actually a race read as there's nothing that guarantees the atomicity of integer
|
memory_order_acquire, memory_order_relaxed)) {
|
||||||
// reads / writes. However, in practice this "never" happens so until we switch to C++11 this
|
return 0;
|
||||||
// should work fine. The same applies in the other places this idiom is used.
|
|
||||||
int32_t cur_state = rwlock->state; // C++11 relaxed atomic read
|
|
||||||
if (__predict_true(cur_state >= 0)) {
|
|
||||||
// Add as an extra reader.
|
|
||||||
done = __sync_bool_compare_and_swap(&rwlock->state, cur_state, cur_state + 1); // C++11 memory_order_aquire
|
|
||||||
} else {
|
|
||||||
if (!timespec_from_absolute(rel_timeout, abs_timeout)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
}
|
||||||
// Owner holds it in write mode, hang up.
|
} else {
|
||||||
// To avoid losing wake ups the pending_readers update and the state read should be
|
timespec ts;
|
||||||
// sequentially consistent. (currently enforced by __sync_fetch_and_add which creates a full barrier)
|
timespec* rel_timeout = NULL;
|
||||||
__sync_fetch_and_add(&rwlock->pending_readers, 1); // C++11 memory_order_relaxed (if the futex_wait ensures the ordering)
|
|
||||||
int ret = __futex_wait_ex(&rwlock->state, rwlock_is_shared(rwlock), cur_state, rel_timeout);
|
if (abs_timeout_or_null != NULL) {
|
||||||
__sync_fetch_and_sub(&rwlock->pending_readers, 1); // C++11 memory_order_relaxed
|
rel_timeout = &ts;
|
||||||
|
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To avoid losing wake ups, the pending_readers increment should be observed before
|
||||||
|
// futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used
|
||||||
|
// here. Because only a seq_cst fence can ensure sequential consistency for non-atomic
|
||||||
|
// operations in futex_wait.
|
||||||
|
atomic_fetch_add_explicit(&rwlock->pending_readers, 1, memory_order_relaxed);
|
||||||
|
|
||||||
|
atomic_thread_fence(memory_order_seq_cst);
|
||||||
|
|
||||||
|
int ret = __futex_wait_ex(&rwlock->state, rwlock->process_shared(), old_state,
|
||||||
|
rel_timeout);
|
||||||
|
|
||||||
|
atomic_fetch_sub_explicit(&rwlock->pending_readers, 1, memory_order_relaxed);
|
||||||
|
|
||||||
if (ret == -ETIMEDOUT) {
|
if (ret == -ETIMEDOUT) {
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (!done);
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock, const timespec* abs_timeout) {
|
static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
||||||
int tid = __get_thread()->tid;
|
const timespec* abs_timeout_or_null) {
|
||||||
if (__predict_false(tid == rwlock->writer_thread_id)) {
|
|
||||||
|
if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id,
|
||||||
|
memory_order_relaxed))) {
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec ts;
|
while (true) {
|
||||||
timespec* rel_timeout = (abs_timeout == NULL) ? NULL : &ts;
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
bool done = false;
|
if (__predict_true(old_state == 0)) {
|
||||||
do {
|
if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1,
|
||||||
int32_t cur_state = rwlock->state;
|
memory_order_acquire, memory_order_relaxed)) {
|
||||||
if (__predict_true(cur_state == 0)) {
|
// writer_thread_id is protected by rwlock and can only be modified in rwlock write
|
||||||
// Change state from 0 to -1.
|
// owner thread. Other threads may read it for EDEADLK error checking, atomic operation
|
||||||
done = __sync_bool_compare_and_swap(&rwlock->state, 0 /* cur state */, -1 /* new state */); // C++11 memory_order_aquire
|
// is safe enough for it.
|
||||||
} else {
|
atomic_store_explicit(&rwlock->writer_thread_id, __get_thread()->tid, memory_order_relaxed);
|
||||||
if (!timespec_from_absolute(rel_timeout, abs_timeout)) {
|
return 0;
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
}
|
||||||
// Failed to acquire, hang up.
|
} else {
|
||||||
// To avoid losing wake ups the pending_writers update and the state read should be
|
timespec ts;
|
||||||
// sequentially consistent. (currently enforced by __sync_fetch_and_add which creates a full barrier)
|
timespec* rel_timeout = NULL;
|
||||||
__sync_fetch_and_add(&rwlock->pending_writers, 1); // C++11 memory_order_relaxed (if the futex_wait ensures the ordering)
|
|
||||||
int ret = __futex_wait_ex(&rwlock->state, rwlock_is_shared(rwlock), cur_state, rel_timeout);
|
if (abs_timeout_or_null != NULL) {
|
||||||
__sync_fetch_and_sub(&rwlock->pending_writers, 1); // C++11 memory_order_relaxed
|
rel_timeout = &ts;
|
||||||
|
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To avoid losing wake ups, the pending_writers increment should be observed before
|
||||||
|
// futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used
|
||||||
|
// here. Because only a seq_cst fence can ensure sequential consistency for non-atomic
|
||||||
|
// operations in futex_wait.
|
||||||
|
atomic_fetch_add_explicit(&rwlock->pending_writers, 1, memory_order_relaxed);
|
||||||
|
|
||||||
|
atomic_thread_fence(memory_order_seq_cst);
|
||||||
|
|
||||||
|
int ret = __futex_wait_ex(&rwlock->state, rwlock->process_shared(), old_state,
|
||||||
|
rel_timeout);
|
||||||
|
|
||||||
|
atomic_fetch_sub_explicit(&rwlock->pending_writers, 1, memory_order_relaxed);
|
||||||
|
|
||||||
if (ret == -ETIMEDOUT) {
|
if (ret == -ETIMEDOUT) {
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (!done);
|
}
|
||||||
|
|
||||||
rwlock->writer_thread_id = tid;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) {
|
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
return __pthread_rwlock_timedrdlock(rwlock, NULL);
|
return __pthread_rwlock_timedrdlock(rwlock, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock, const timespec* abs_timeout) {
|
int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
|
||||||
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
return __pthread_rwlock_timedrdlock(rwlock, abs_timeout);
|
return __pthread_rwlock_timedrdlock(rwlock, abs_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) {
|
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
int32_t cur_state = rwlock->state;
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
if ((cur_state >= 0) &&
|
|
||||||
__sync_bool_compare_and_swap(&rwlock->state, cur_state, cur_state + 1)) { // C++11 memory_order_acquire
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
return 0;
|
|
||||||
|
while (old_state >= 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state,
|
||||||
|
old_state + 1, memory_order_acquire, memory_order_relaxed)) {
|
||||||
}
|
}
|
||||||
return EBUSY;
|
return (old_state >= 0) ? 0 : EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) {
|
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
return __pthread_rwlock_timedwrlock(rwlock, NULL);
|
return __pthread_rwlock_timedwrlock(rwlock, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock, const timespec* abs_timeout) {
|
int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
|
||||||
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
return __pthread_rwlock_timedwrlock(rwlock, abs_timeout);
|
return __pthread_rwlock_timedwrlock(rwlock, abs_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) {
|
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
int tid = __get_thread()->tid;
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
int32_t cur_state = rwlock->state;
|
|
||||||
if ((cur_state == 0) &&
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
__sync_bool_compare_and_swap(&rwlock->state, 0 /* cur state */, -1 /* new state */)) { // C++11 memory_order_acquire
|
|
||||||
rwlock->writer_thread_id = tid;
|
while (old_state == 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1,
|
||||||
|
memory_order_acquire, memory_order_relaxed)) {
|
||||||
|
}
|
||||||
|
if (old_state == 0) {
|
||||||
|
atomic_store_explicit(&rwlock->writer_thread_id, __get_thread()->tid, memory_order_relaxed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) {
|
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
int tid = __get_thread()->tid;
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
bool done = false;
|
|
||||||
do {
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
int32_t cur_state = rwlock->state;
|
if (__predict_false(old_state == 0)) {
|
||||||
if (cur_state == 0) {
|
return EPERM;
|
||||||
|
} else if (old_state == -1) {
|
||||||
|
if (atomic_load_explicit(&rwlock->writer_thread_id, memory_order_relaxed) != __get_thread()->tid) {
|
||||||
return EPERM;
|
return EPERM;
|
||||||
}
|
}
|
||||||
if (cur_state == -1) {
|
// We're no longer the owner.
|
||||||
if (rwlock->writer_thread_id != tid) {
|
atomic_store_explicit(&rwlock->writer_thread_id, 0, memory_order_relaxed);
|
||||||
return EPERM;
|
// Change state from -1 to 0.
|
||||||
}
|
atomic_store_explicit(&rwlock->state, 0, memory_order_release);
|
||||||
// We're no longer the owner.
|
|
||||||
rwlock->writer_thread_id = 0;
|
|
||||||
// Change state from -1 to 0.
|
|
||||||
// We use __sync_bool_compare_and_swap to achieve sequential consistency of the state store and
|
|
||||||
// the following pendingX loads. A simple store with memory_order_release semantics
|
|
||||||
// is not enough to guarantee that the pendingX loads are not reordered before the
|
|
||||||
// store (which may lead to a lost wakeup).
|
|
||||||
__sync_bool_compare_and_swap( &rwlock->state, -1 /* cur state*/, 0 /* new state */); // C++11 maybe memory_order_seq_cst?
|
|
||||||
|
|
||||||
// Wake any waiters.
|
} else { // old_state > 0
|
||||||
if (__predict_false(rwlock->pending_readers > 0 || rwlock->pending_writers > 0)) {
|
// Reduce state by 1.
|
||||||
__futex_wake_ex(&rwlock->state, rwlock_is_shared(rwlock), INT_MAX);
|
while (old_state > 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state,
|
||||||
}
|
old_state - 1, memory_order_release, memory_order_relaxed)) {
|
||||||
done = true;
|
|
||||||
} else { // cur_state > 0
|
|
||||||
// Reduce state by 1.
|
|
||||||
// See the comment above on why we need __sync_bool_compare_and_swap.
|
|
||||||
done = __sync_bool_compare_and_swap(&rwlock->state, cur_state, cur_state - 1); // C++11 maybe memory_order_seq_cst?
|
|
||||||
if (done && (cur_state - 1) == 0) {
|
|
||||||
// There are no more readers, wake any waiters.
|
|
||||||
if (__predict_false(rwlock->pending_readers > 0 || rwlock->pending_writers > 0)) {
|
|
||||||
__futex_wake_ex(&rwlock->state, rwlock_is_shared(rwlock), INT_MAX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} while (!done);
|
|
||||||
|
|
||||||
|
if (old_state <= 0) {
|
||||||
|
return EPERM;
|
||||||
|
} else if (old_state > 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// old_state = 1, which means the last reader calling unlock. It has to wake up waiters.
|
||||||
|
}
|
||||||
|
|
||||||
|
// If having waiters, wake up them.
|
||||||
|
// To avoid losing wake ups, the update of state should be observed before reading
|
||||||
|
// pending_readers/pending_writers by all threads. Use read locking as an example:
|
||||||
|
// read locking thread unlocking thread
|
||||||
|
// pending_readers++; state = 0;
|
||||||
|
// seq_cst fence seq_cst fence
|
||||||
|
// read state for futex_wait read pending_readers for futex_wake
|
||||||
|
//
|
||||||
|
// So when locking and unlocking threads are running in parallel, we will not get
|
||||||
|
// in a situation that the locking thread reads state as negative and needs to wait,
|
||||||
|
// while the unlocking thread reads pending_readers as zero and doesn't need to wake up waiters.
|
||||||
|
atomic_thread_fence(memory_order_seq_cst);
|
||||||
|
if (__predict_false(atomic_load_explicit(&rwlock->pending_readers, memory_order_relaxed) > 0 ||
|
||||||
|
atomic_load_explicit(&rwlock->pending_writers, memory_order_relaxed) > 0)) {
|
||||||
|
__futex_wake_ex(&rwlock->state, rwlock->process_shared(), INT_MAX);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -446,31 +446,6 @@ protoent* getprotobynumber(int /*proto*/) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unimplemented_stub(const char* function) {
|
|
||||||
const char* fmt = "%s(3) is not implemented on Android\n";
|
|
||||||
__libc_format_log(ANDROID_LOG_WARN, "libc", fmt, function);
|
|
||||||
fprintf(stderr, fmt, function);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define UNIMPLEMENTED unimplemented_stub(__PRETTY_FUNCTION__)
|
|
||||||
|
|
||||||
void endpwent() {
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* getusershell() {
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setusershell() {
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void endusershell() {
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Portable code should use sysconf(_SC_PAGE_SIZE) directly instead.
|
// Portable code should use sysconf(_SC_PAGE_SIZE) directly instead.
|
||||||
int getpagesize() {
|
int getpagesize() {
|
||||||
// We dont use sysconf(3) here because that drags in stdio, which makes static binaries fat.
|
// We dont use sysconf(3) here because that drags in stdio, which makes static binaries fat.
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h> // For FOPEN_MAX.
|
#include <stdio.h> // For FOPEN_MAX.
|
||||||
#include <sys/auxv.h>
|
#include <sys/auxv.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include <sys/sysconf.h>
|
#include <sys/sysconf.h>
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -50,6 +51,12 @@ static bool __sysconf_has_clock(clockid_t clock_id) {
|
|||||||
return clock_getres(clock_id, NULL) == 0;
|
return clock_getres(clock_id, NULL) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long __sysconf_rlimit(int resource) {
|
||||||
|
rlimit rl;
|
||||||
|
getrlimit(resource, &rl);
|
||||||
|
return rl.rlim_cur;
|
||||||
|
}
|
||||||
|
|
||||||
long sysconf(int name) {
|
long sysconf(int name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case _SC_ARG_MAX: return ARG_MAX;
|
case _SC_ARG_MAX: return ARG_MAX;
|
||||||
@ -57,13 +64,13 @@ long sysconf(int name) {
|
|||||||
case _SC_BC_DIM_MAX: return _POSIX2_BC_DIM_MAX; // Minimum requirement.
|
case _SC_BC_DIM_MAX: return _POSIX2_BC_DIM_MAX; // Minimum requirement.
|
||||||
case _SC_BC_SCALE_MAX: return _POSIX2_BC_SCALE_MAX; // Minimum requirement.
|
case _SC_BC_SCALE_MAX: return _POSIX2_BC_SCALE_MAX; // Minimum requirement.
|
||||||
case _SC_BC_STRING_MAX: return _POSIX2_BC_STRING_MAX; // Minimum requirement.
|
case _SC_BC_STRING_MAX: return _POSIX2_BC_STRING_MAX; // Minimum requirement.
|
||||||
case _SC_CHILD_MAX: return CHILD_MAX;
|
case _SC_CHILD_MAX: return __sysconf_rlimit(RLIMIT_NPROC);
|
||||||
case _SC_CLK_TCK: return static_cast<long>(getauxval(AT_CLKTCK));
|
case _SC_CLK_TCK: return static_cast<long>(getauxval(AT_CLKTCK));
|
||||||
case _SC_COLL_WEIGHTS_MAX: return _POSIX2_COLL_WEIGHTS_MAX; // Minimum requirement.
|
case _SC_COLL_WEIGHTS_MAX: return _POSIX2_COLL_WEIGHTS_MAX; // Minimum requirement.
|
||||||
case _SC_EXPR_NEST_MAX: return _POSIX2_EXPR_NEST_MAX; // Minimum requirement.
|
case _SC_EXPR_NEST_MAX: return _POSIX2_EXPR_NEST_MAX; // Minimum requirement.
|
||||||
case _SC_LINE_MAX: return _POSIX2_LINE_MAX; // Minimum requirement.
|
case _SC_LINE_MAX: return _POSIX2_LINE_MAX; // Minimum requirement.
|
||||||
case _SC_NGROUPS_MAX: return NGROUPS_MAX;
|
case _SC_NGROUPS_MAX: return NGROUPS_MAX;
|
||||||
case _SC_OPEN_MAX: return OPEN_MAX;
|
case _SC_OPEN_MAX: return __sysconf_rlimit(RLIMIT_NOFILE);
|
||||||
case _SC_PASS_MAX: return PASS_MAX;
|
case _SC_PASS_MAX: return PASS_MAX;
|
||||||
case _SC_2_C_BIND: return _POSIX2_C_BIND;
|
case _SC_2_C_BIND: return _POSIX2_C_BIND;
|
||||||
case _SC_2_C_DEV: return _POSIX2_C_DEV;
|
case _SC_2_C_DEV: return _POSIX2_C_DEV;
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
#include <sys/_system_properties.h>
|
#include <sys/_system_properties.h>
|
||||||
#include <sys/system_properties.h>
|
#include <sys/system_properties.h>
|
||||||
|
|
||||||
#include "private/bionic_atomic_inline.h"
|
|
||||||
#include "private/bionic_futex.h"
|
#include "private/bionic_futex.h"
|
||||||
#include "private/bionic_macros.h"
|
#include "private/bionic_macros.h"
|
||||||
|
|
||||||
@ -80,22 +79,26 @@ struct prop_bt {
|
|||||||
uint8_t namelen;
|
uint8_t namelen;
|
||||||
uint8_t reserved[3];
|
uint8_t reserved[3];
|
||||||
|
|
||||||
// TODO: The following fields should be declared as atomic_uint32_t.
|
// The property trie is updated only by the init process (single threaded) which provides
|
||||||
// They should be assigned to with release semantics, instead of using
|
// property service. And it can be read by multiple threads at the same time.
|
||||||
// explicit fences. Unfortunately, the read accesses are generally
|
// As the property trie is not protected by locks, we use atomic_uint_least32_t types for the
|
||||||
// followed by more dependent read accesses, and the dependence
|
// left, right, children "pointers" in the trie node. To make sure readers who see the
|
||||||
// is assumed to enforce memory ordering. Which it does on supported
|
// change of "pointers" can also notice the change of prop_bt structure contents pointed by
|
||||||
// hardware. This technically should use memory_order_consume, if
|
// the "pointers", we always use release-consume ordering pair when accessing these "pointers".
|
||||||
// that worked as intended.
|
|
||||||
|
// prop "points" to prop_info structure if there is a propery associated with the trie node.
|
||||||
|
// Its situation is similar to the left, right, children "pointers". So we use
|
||||||
|
// atomic_uint_least32_t and release-consume ordering to protect it as well.
|
||||||
|
|
||||||
// We should also avoid rereading these fields redundantly, since not
|
// We should also avoid rereading these fields redundantly, since not
|
||||||
// all processor implementations ensure that multiple loads from the
|
// all processor implementations ensure that multiple loads from the
|
||||||
// same field are carried out in the right order.
|
// same field are carried out in the right order.
|
||||||
volatile uint32_t prop;
|
atomic_uint_least32_t prop;
|
||||||
|
|
||||||
volatile uint32_t left;
|
atomic_uint_least32_t left;
|
||||||
volatile uint32_t right;
|
atomic_uint_least32_t right;
|
||||||
|
|
||||||
volatile uint32_t children;
|
atomic_uint_least32_t children;
|
||||||
|
|
||||||
char name[0];
|
char name[0];
|
||||||
|
|
||||||
@ -103,8 +106,6 @@ struct prop_bt {
|
|||||||
this->namelen = name_length;
|
this->namelen = name_length;
|
||||||
memcpy(this->name, name, name_length);
|
memcpy(this->name, name, name_length);
|
||||||
this->name[name_length] = '\0';
|
this->name[name_length] = '\0';
|
||||||
ANDROID_MEMBAR_FULL(); // TODO: Instead use a release store
|
|
||||||
// for subsequent pointer assignment.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -143,8 +144,6 @@ struct prop_info {
|
|||||||
atomic_init(&this->serial, valuelen << 24);
|
atomic_init(&this->serial, valuelen << 24);
|
||||||
memcpy(this->value, value, valuelen);
|
memcpy(this->value, value, valuelen);
|
||||||
this->value[valuelen] = '\0';
|
this->value[valuelen] = '\0';
|
||||||
ANDROID_MEMBAR_FULL(); // TODO: Instead use a release store
|
|
||||||
// for subsequent point assignment.
|
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(prop_info);
|
DISALLOW_COPY_AND_ASSIGN(prop_info);
|
||||||
@ -291,10 +290,10 @@ static int map_prop_area()
|
|||||||
return map_result;
|
return map_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *allocate_obj(const size_t size, uint32_t *const off)
|
static void *allocate_obj(const size_t size, uint_least32_t *const off)
|
||||||
{
|
{
|
||||||
prop_area *pa = __system_property_area__;
|
prop_area *pa = __system_property_area__;
|
||||||
const size_t aligned = BIONIC_ALIGN(size, sizeof(uint32_t));
|
const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t));
|
||||||
if (pa->bytes_used + aligned > pa_data_size) {
|
if (pa->bytes_used + aligned > pa_data_size) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -304,12 +303,12 @@ static void *allocate_obj(const size_t size, uint32_t *const off)
|
|||||||
return pa->data + *off;
|
return pa->data + *off;
|
||||||
}
|
}
|
||||||
|
|
||||||
static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint32_t *const off)
|
static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off)
|
||||||
{
|
{
|
||||||
uint32_t new_offset;
|
uint_least32_t new_offset;
|
||||||
void *const offset = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
|
void *const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
|
||||||
if (offset) {
|
if (p != NULL) {
|
||||||
prop_bt* bt = new(offset) prop_bt(name, namelen);
|
prop_bt* bt = new(p) prop_bt(name, namelen);
|
||||||
*off = new_offset;
|
*off = new_offset;
|
||||||
return bt;
|
return bt;
|
||||||
}
|
}
|
||||||
@ -318,20 +317,20 @@ static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint32_t *const o
|
|||||||
}
|
}
|
||||||
|
|
||||||
static prop_info *new_prop_info(const char *name, uint8_t namelen,
|
static prop_info *new_prop_info(const char *name, uint8_t namelen,
|
||||||
const char *value, uint8_t valuelen, uint32_t *const off)
|
const char *value, uint8_t valuelen, uint_least32_t *const off)
|
||||||
{
|
{
|
||||||
uint32_t off_tmp;
|
uint_least32_t new_offset;
|
||||||
void* const offset = allocate_obj(sizeof(prop_info) + namelen + 1, &off_tmp);
|
void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
|
||||||
if (offset) {
|
if (p != NULL) {
|
||||||
prop_info* info = new(offset) prop_info(name, namelen, value, valuelen);
|
prop_info* info = new(p) prop_info(name, namelen, value, valuelen);
|
||||||
*off = off_tmp;
|
*off = new_offset;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *to_prop_obj(const uint32_t off)
|
static void *to_prop_obj(uint_least32_t off)
|
||||||
{
|
{
|
||||||
if (off > pa_data_size)
|
if (off > pa_data_size)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -341,7 +340,17 @@ static void *to_prop_obj(const uint32_t off)
|
|||||||
return (__system_property_area__->data + off);
|
return (__system_property_area__->data + off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static prop_bt *root_node()
|
static inline prop_bt *to_prop_bt(atomic_uint_least32_t* off_p) {
|
||||||
|
uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
|
||||||
|
return reinterpret_cast<prop_bt*>(to_prop_obj(off));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline prop_info *to_prop_info(atomic_uint_least32_t* off_p) {
|
||||||
|
uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
|
||||||
|
return reinterpret_cast<prop_info*>(to_prop_obj(off));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline prop_bt *root_node()
|
||||||
{
|
{
|
||||||
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
|
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
|
||||||
}
|
}
|
||||||
@ -373,36 +382,34 @@ static prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (current->left) {
|
uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed);
|
||||||
current = reinterpret_cast<prop_bt*>(to_prop_obj(current->left));
|
if (left_offset != 0) {
|
||||||
|
current = to_prop_bt(¤t->left);
|
||||||
} else {
|
} else {
|
||||||
if (!alloc_if_needed) {
|
if (!alloc_if_needed) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that there isn't a race condition here. "clients" never
|
uint_least32_t new_offset;
|
||||||
// reach this code-path since It's only the (single threaded) server
|
|
||||||
// that allocates new nodes. Though "bt->left" is volatile, it can't
|
|
||||||
// have changed since the last value was last read.
|
|
||||||
uint32_t new_offset = 0;
|
|
||||||
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
|
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
|
||||||
if (new_bt) {
|
if (new_bt) {
|
||||||
current->left = new_offset;
|
atomic_store_explicit(¤t->left, new_offset, memory_order_release);
|
||||||
}
|
}
|
||||||
return new_bt;
|
return new_bt;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (current->right) {
|
uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
|
||||||
current = reinterpret_cast<prop_bt*>(to_prop_obj(current->right));
|
if (right_offset != 0) {
|
||||||
|
current = to_prop_bt(¤t->right);
|
||||||
} else {
|
} else {
|
||||||
if (!alloc_if_needed) {
|
if (!alloc_if_needed) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t new_offset;
|
uint_least32_t new_offset;
|
||||||
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
|
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
|
||||||
if (new_bt) {
|
if (new_bt) {
|
||||||
current->right = new_offset;
|
atomic_store_explicit(¤t->right, new_offset, memory_order_release);
|
||||||
}
|
}
|
||||||
return new_bt;
|
return new_bt;
|
||||||
}
|
}
|
||||||
@ -429,13 +436,14 @@ static const prop_info *find_property(prop_bt *const trie, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
prop_bt* root = NULL;
|
prop_bt* root = NULL;
|
||||||
if (current->children) {
|
uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
|
||||||
root = reinterpret_cast<prop_bt*>(to_prop_obj(current->children));
|
if (children_offset != 0) {
|
||||||
|
root = to_prop_bt(¤t->children);
|
||||||
} else if (alloc_if_needed) {
|
} else if (alloc_if_needed) {
|
||||||
uint32_t new_bt_offset;
|
uint_least32_t new_offset;
|
||||||
root = new_prop_bt(remaining_name, substr_size, &new_bt_offset);
|
root = new_prop_bt(remaining_name, substr_size, &new_offset);
|
||||||
if (root) {
|
if (root) {
|
||||||
current->children = new_bt_offset;
|
atomic_store_explicit(¤t->children, new_offset, memory_order_release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,13 +462,14 @@ static const prop_info *find_property(prop_bt *const trie, const char *name,
|
|||||||
remaining_name = sep + 1;
|
remaining_name = sep + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current->prop) {
|
uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
|
||||||
return reinterpret_cast<prop_info*>(to_prop_obj(current->prop));
|
if (prop_offset != 0) {
|
||||||
|
return to_prop_info(¤t->prop);
|
||||||
} else if (alloc_if_needed) {
|
} else if (alloc_if_needed) {
|
||||||
uint32_t new_info_offset;
|
uint_least32_t new_offset;
|
||||||
prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_info_offset);
|
prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
|
||||||
if (new_info) {
|
if (new_info) {
|
||||||
current->prop = new_info_offset;
|
atomic_store_explicit(¤t->prop, new_offset, memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_info;
|
return new_info;
|
||||||
@ -534,31 +543,34 @@ static void find_nth_fn(const prop_info *pi, void *ptr)
|
|||||||
cookie->count++;
|
cookie->count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int foreach_property(const uint32_t off,
|
static int foreach_property(prop_bt *const trie,
|
||||||
void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
|
void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
|
||||||
{
|
{
|
||||||
prop_bt *trie = reinterpret_cast<prop_bt*>(to_prop_obj(off));
|
|
||||||
if (!trie)
|
if (!trie)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (trie->left) {
|
uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed);
|
||||||
const int err = foreach_property(trie->left, propfn, cookie);
|
if (left_offset != 0) {
|
||||||
|
const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (trie->prop) {
|
uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
|
||||||
prop_info *info = reinterpret_cast<prop_info*>(to_prop_obj(trie->prop));
|
if (prop_offset != 0) {
|
||||||
|
prop_info *info = to_prop_info(&trie->prop);
|
||||||
if (!info)
|
if (!info)
|
||||||
return -1;
|
return -1;
|
||||||
propfn(info, cookie);
|
propfn(info, cookie);
|
||||||
}
|
}
|
||||||
if (trie->children) {
|
uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed);
|
||||||
const int err = foreach_property(trie->children, propfn, cookie);
|
if (children_offset != 0) {
|
||||||
|
const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (trie->right) {
|
uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed);
|
||||||
const int err = foreach_property(trie->right, propfn, cookie);
|
if (right_offset != 0) {
|
||||||
|
const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -766,5 +778,5 @@ int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
|
|||||||
return __system_property_foreach_compat(propfn, cookie);
|
return __system_property_foreach_compat(propfn, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
return foreach_property(0, propfn, cookie);
|
return foreach_property(root_node(), propfn, cookie);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -25,23 +25,9 @@
|
|||||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
void *memrchr(const void *s, int c, size_t n)
|
#include <wchar.h>
|
||||||
{
|
|
||||||
if (n > 0) {
|
|
||||||
const char* p = (const char*) s;
|
|
||||||
const char* q = p + n;
|
|
||||||
|
|
||||||
while (1) {
|
wchar_t* wmempcpy(wchar_t* dst, const wchar_t* src, size_t n) {
|
||||||
q--; if (q < p || q[0] == (char) c) break;
|
return wmemcpy(dst, src, n) + n;
|
||||||
q--; if (q < p || q[0] == (char) c) break;
|
|
||||||
q--; if (q < p || q[0] == (char) c) break;
|
|
||||||
q--; if (q < p || q[0] == (char) c) break;
|
|
||||||
}
|
|
||||||
if (q >= p)
|
|
||||||
return (void*)q;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
@ -402,6 +402,10 @@ res_nsend(res_state statp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (statp->nscount == 0) {
|
if (statp->nscount == 0) {
|
||||||
|
// We have no nameservers configured, so there's no point trying.
|
||||||
|
// Tell the cache the query failed, or any retries and anyone else asking the same
|
||||||
|
// question will block for PENDING_REQUEST_TIMEOUT seconds instead of failing fast.
|
||||||
|
_resolv_cache_query_failed(statp->netid, buf, buflen);
|
||||||
errno = ESRCH;
|
errno = ESRCH;
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@
|
|||||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||||
#include <sys/_system_properties.h>
|
#include <sys/_system_properties.h>
|
||||||
|
|
||||||
|
#include "private/ThreadLocalBuffer.h"
|
||||||
|
|
||||||
/* Set to 1 to enable debug traces */
|
/* Set to 1 to enable debug traces */
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
|
|
||||||
@ -50,8 +52,6 @@
|
|||||||
# define D(...) do{}while(0)
|
# define D(...) do{}while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static pthread_key_t _res_key;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int _h_errno;
|
int _h_errno;
|
||||||
// TODO: Have one __res_state per network so we don't have to repopulate frequently.
|
// TODO: Have one __res_state per network so we don't have to repopulate frequently.
|
||||||
@ -105,12 +105,7 @@ _res_thread_free( void* _rt )
|
|||||||
free(rt);
|
free(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((constructor))
|
BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR(_res_key, _res_thread_free);
|
||||||
static void
|
|
||||||
_res_init_key( void )
|
|
||||||
{
|
|
||||||
pthread_key_create( &_res_key, _res_thread_free );
|
|
||||||
}
|
|
||||||
|
|
||||||
static _res_thread*
|
static _res_thread*
|
||||||
_res_thread_get(void)
|
_res_thread_get(void)
|
||||||
|
@ -94,6 +94,13 @@ typedef struct {
|
|||||||
#define DT_PREINIT_ARRAY 32
|
#define DT_PREINIT_ARRAY 32
|
||||||
#define DT_PREINIT_ARRAYSZ 33
|
#define DT_PREINIT_ARRAYSZ 33
|
||||||
|
|
||||||
|
/* Android compressed rel/rela sections */
|
||||||
|
#define DT_ANDROID_REL (DT_LOOS + 2)
|
||||||
|
#define DT_ANDROID_RELSZ (DT_LOOS + 3)
|
||||||
|
|
||||||
|
#define DT_ANDROID_RELA (DT_LOOS + 4)
|
||||||
|
#define DT_ANDROID_RELASZ (DT_LOOS + 5)
|
||||||
|
|
||||||
/* gnu hash entry */
|
/* gnu hash entry */
|
||||||
#define DT_GNU_HASH 0x6ffffef5
|
#define DT_GNU_HASH 0x6ffffef5
|
||||||
|
|
||||||
@ -106,6 +113,9 @@ typedef struct {
|
|||||||
#define STB_LOPROC 13
|
#define STB_LOPROC 13
|
||||||
#define STB_HIPROC 15
|
#define STB_HIPROC 15
|
||||||
|
|
||||||
|
#define SHT_LOOS 0x60000000
|
||||||
|
#define SHT_HIOS 0x6fffffff
|
||||||
|
|
||||||
#define STT_GNU_IFUNC 10
|
#define STT_GNU_IFUNC 10
|
||||||
#define STT_LOOS 10
|
#define STT_LOOS 10
|
||||||
#define STT_HIOS 12
|
#define STT_HIOS 12
|
||||||
@ -115,4 +125,8 @@ typedef struct {
|
|||||||
/* The kernel uses NT_PRFPREG but glibc also offers NT_FPREGSET */
|
/* The kernel uses NT_PRFPREG but glibc also offers NT_FPREGSET */
|
||||||
#define NT_FPREGSET NT_PRFPREG
|
#define NT_FPREGSET NT_PRFPREG
|
||||||
|
|
||||||
|
#define ELF_NOTE_GNU "GNU"
|
||||||
|
|
||||||
|
#define NT_GNU_BUILD_ID 3
|
||||||
|
|
||||||
#endif /* _ELF_H */
|
#endif /* _ELF_H */
|
||||||
|
@ -59,22 +59,29 @@ __BEGIN_DECLS
|
|||||||
|
|
||||||
extern int creat(const char*, mode_t);
|
extern int creat(const char*, mode_t);
|
||||||
extern int creat64(const char*, mode_t);
|
extern int creat64(const char*, mode_t);
|
||||||
extern int fallocate64(int, int, off64_t, off64_t);
|
|
||||||
extern int fallocate(int, int, off_t, off_t);
|
|
||||||
extern int fcntl(int, int, ...);
|
extern int fcntl(int, int, ...);
|
||||||
extern int openat(int, const char*, int, ...);
|
extern int openat(int, const char*, int, ...);
|
||||||
extern int openat64(int, const char*, int, ...);
|
extern int openat64(int, const char*, int, ...);
|
||||||
extern int open(const char*, int, ...);
|
extern int open(const char*, int, ...);
|
||||||
extern int open64(const char*, int, ...);
|
extern int open64(const char*, int, ...);
|
||||||
extern int posix_fadvise64(int, off64_t, off64_t, int);
|
|
||||||
extern int posix_fadvise(int, off_t, off_t, int);
|
|
||||||
extern int posix_fallocate64(int, off64_t, off64_t);
|
|
||||||
extern int posix_fallocate(int, off_t, off_t);
|
|
||||||
extern ssize_t splice(int, off64_t*, int, off64_t*, size_t, unsigned int);
|
extern ssize_t splice(int, off64_t*, int, off64_t*, size_t, unsigned int);
|
||||||
extern ssize_t tee(int, int, size_t, unsigned int);
|
extern ssize_t tee(int, int, size_t, unsigned int);
|
||||||
extern int unlinkat(int, const char*, int);
|
extern int unlinkat(int, const char*, int);
|
||||||
extern ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int);
|
extern ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int);
|
||||||
|
|
||||||
|
#if defined(__USE_FILE_OFFSET64)
|
||||||
|
extern int fallocate(int, int, off_t, off_t) __RENAME(fallocate64);
|
||||||
|
extern int posix_fadvise(int, off_t, off_t, int) __RENAME(posix_fadvise64);
|
||||||
|
extern int posix_fallocate(int, off_t, off_t) __RENAME(posix_fallocate);
|
||||||
|
#else
|
||||||
|
extern int fallocate(int, int, off_t, off_t);
|
||||||
|
extern int posix_fadvise(int, off_t, off_t, int);
|
||||||
|
extern int posix_fallocate(int, off_t, off_t);
|
||||||
|
#endif
|
||||||
|
extern int fallocate64(int, int, off64_t, off64_t);
|
||||||
|
extern int posix_fadvise64(int, off64_t, off64_t, int);
|
||||||
|
extern int posix_fallocate64(int, off64_t, off64_t);
|
||||||
|
|
||||||
extern int __open_2(const char*, int);
|
extern int __open_2(const char*, int);
|
||||||
extern int __open_real(const char*, int, ...) __RENAME(open);
|
extern int __open_real(const char*, int, ...) __RENAME(open);
|
||||||
extern int __openat_2(int, const char*, int);
|
extern int __openat_2(int, const char*, int);
|
||||||
|
@ -54,9 +54,9 @@ __BEGIN_DECLS
|
|||||||
struct group *getgrgid(gid_t);
|
struct group *getgrgid(gid_t);
|
||||||
struct group *getgrnam(const char *);
|
struct group *getgrnam(const char *);
|
||||||
#if __POSIX_VISIBLE >= 200112 || __XPG_VISIBLE
|
#if __POSIX_VISIBLE >= 200112 || __XPG_VISIBLE
|
||||||
struct group *getgrent(void);
|
struct group *getgrent(void) __attribute__((deprecated("getgrent is meaningless on Android")));
|
||||||
void setgrent(void);
|
void setgrent(void) __attribute__((deprecated("setgrent is meaningless on Android")));
|
||||||
void endgrent(void);
|
void endgrent(void) __attribute__((deprecated("endgrent is meaningless on Android")));
|
||||||
int getgrgid_r(gid_t, struct group *, char *,
|
int getgrgid_r(gid_t, struct group *, char *,
|
||||||
size_t, struct group **);
|
size_t, struct group **);
|
||||||
int getgrnam_r(const char *, struct group *, char *,
|
int getgrnam_r(const char *, struct group *, char *,
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
#define _PATH_CONSOLE "/dev/console"
|
#define _PATH_CONSOLE "/dev/console"
|
||||||
#define _PATH_DEVNULL "/dev/null"
|
#define _PATH_DEVNULL "/dev/null"
|
||||||
#define _PATH_KLOG "/proc/kmsg"
|
#define _PATH_KLOG "/proc/kmsg"
|
||||||
#define _PATH_MEM "/dev/mem"
|
|
||||||
|
|
||||||
#define _PATH_MOUNTED "/proc/mounts"
|
#define _PATH_MOUNTED "/proc/mounts"
|
||||||
#define _PATH_TTY "/dev/tty"
|
#define _PATH_TTY "/dev/tty"
|
||||||
|
@ -73,13 +73,14 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int value;
|
#if defined(__LP64__)
|
||||||
#ifdef __LP64__
|
char __private[48];
|
||||||
char __reserved[44];
|
#else
|
||||||
|
char __private[4];
|
||||||
#endif
|
#endif
|
||||||
} pthread_cond_t;
|
} pthread_cond_t __attribute__((aligned(8)));
|
||||||
|
|
||||||
#define PTHREAD_COND_INITIALIZER {0 __RESERVED_INITIALIZER}
|
#define PTHREAD_COND_INITIALIZER { { 0 } }
|
||||||
|
|
||||||
typedef long pthread_mutexattr_t;
|
typedef long pthread_mutexattr_t;
|
||||||
typedef long pthread_condattr_t;
|
typedef long pthread_condattr_t;
|
||||||
@ -87,28 +88,14 @@ typedef long pthread_condattr_t;
|
|||||||
typedef long pthread_rwlockattr_t;
|
typedef long pthread_rwlockattr_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#if !defined(__LP64__)
|
#if defined(__LP64__)
|
||||||
pthread_mutex_t __unused_lock;
|
char __private[56];
|
||||||
pthread_cond_t __unused_cond;
|
|
||||||
#endif
|
|
||||||
volatile int32_t state; // 0=unlock, -1=writer lock, +n=reader lock
|
|
||||||
volatile int32_t writer_thread_id;
|
|
||||||
volatile int32_t pending_readers;
|
|
||||||
volatile int32_t pending_writers;
|
|
||||||
int32_t attr;
|
|
||||||
#ifdef __LP64__
|
|
||||||
char __reserved[36];
|
|
||||||
#else
|
#else
|
||||||
char __reserved[12];
|
char __private[40];
|
||||||
#endif
|
#endif
|
||||||
|
} pthread_rwlock_t __attribute__((aligned(8)));
|
||||||
|
|
||||||
} pthread_rwlock_t;
|
#define PTHREAD_RWLOCK_INITIALIZER { { 0 } }
|
||||||
|
|
||||||
#ifdef __LP64__
|
|
||||||
#define PTHREAD_RWLOCK_INITIALIZER { 0, 0, 0, 0, 0, { 0 } }
|
|
||||||
#else
|
|
||||||
#define PTHREAD_RWLOCK_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0, 0, 0, 0, { 0 } }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef int pthread_key_t;
|
typedef int pthread_key_t;
|
||||||
|
|
||||||
|
@ -119,10 +119,6 @@ struct passwd* getpwuid(uid_t);
|
|||||||
int getpwnam_r(const char*, struct passwd*, char*, size_t, struct passwd**);
|
int getpwnam_r(const char*, struct passwd*, char*, size_t, struct passwd**);
|
||||||
int getpwuid_r(uid_t, struct passwd*, char*, size_t, struct passwd**);
|
int getpwuid_r(uid_t, struct passwd*, char*, size_t, struct passwd**);
|
||||||
|
|
||||||
void endpwent(void);
|
|
||||||
struct passwd* getpwent(void);
|
|
||||||
int setpwent(void);
|
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,8 +57,6 @@
|
|||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
#define _FSTDIO /* Define for new stdio with functions. */
|
|
||||||
|
|
||||||
typedef off_t fpos_t; /* stdio file position type */
|
typedef off_t fpos_t; /* stdio file position type */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -266,27 +264,38 @@ int vdprintf(int, const char * __restrict, __va_list) __printflike(2, 0);
|
|||||||
|
|
||||||
#ifndef __AUDIT__
|
#ifndef __AUDIT__
|
||||||
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L
|
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L
|
||||||
char* gets(char*) __warnattr("gets is very unsafe; consider using fgets");
|
char* gets(char*) __attribute__((deprecated("gets is unsafe, use fgets instead")));
|
||||||
#endif
|
#endif
|
||||||
int sprintf(char* __restrict, const char* __restrict, ...)
|
int sprintf(char* __restrict, const char* __restrict, ...)
|
||||||
__printflike(2, 3) __warnattr("sprintf is often misused; please use snprintf");
|
__printflike(2, 3) __warnattr("sprintf is often misused; please use snprintf");
|
||||||
char* tmpnam(char*) __warnattr("tmpnam possibly used unsafely; consider using mkstemp");
|
|
||||||
int vsprintf(char* __restrict, const char* __restrict, __va_list)
|
int vsprintf(char* __restrict, const char* __restrict, __va_list)
|
||||||
__printflike(2, 0) __warnattr("vsprintf is often misused; please use vsnprintf");
|
__printflike(2, 0) __warnattr("vsprintf is often misused; please use vsnprintf");
|
||||||
|
char* tmpnam(char*) __attribute__((deprecated("tmpnam is unsafe, use mkstemp or tmpfile instead")));
|
||||||
#if __XPG_VISIBLE
|
#if __XPG_VISIBLE
|
||||||
char* tempnam(const char*, const char*)
|
char* tempnam(const char*, const char*)
|
||||||
__warnattr("tempnam possibly used unsafely; consider using mkstemp");
|
__attribute__((deprecated("tempnam is unsafe, use mkstemp or tmpfile instead")));
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int rename(const char*, const char*);
|
extern int rename(const char*, const char*);
|
||||||
extern int renameat(int, const char*, int, const char*);
|
extern int renameat(int, const char*, int, const char*);
|
||||||
|
|
||||||
|
#if defined(__USE_FILE_OFFSET64)
|
||||||
|
/* Not possible. */
|
||||||
|
int fgetpos(FILE * __restrict, fpos_t * __restrict)
|
||||||
|
__attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
|
||||||
|
int fsetpos(FILE *, const fpos_t *)
|
||||||
|
__attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
|
||||||
|
int fseeko(FILE *, off_t, int)
|
||||||
|
__attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
|
||||||
|
off_t ftello(FILE *)
|
||||||
|
__attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
|
||||||
|
#else
|
||||||
int fgetpos(FILE * __restrict, fpos_t * __restrict);
|
int fgetpos(FILE * __restrict, fpos_t * __restrict);
|
||||||
int fsetpos(FILE *, const fpos_t *);
|
int fsetpos(FILE *, const fpos_t *);
|
||||||
|
|
||||||
int fseeko(FILE *, off_t, int);
|
int fseeko(FILE *, off_t, int);
|
||||||
off_t ftello(FILE *);
|
off_t ftello(FILE *);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __ISO_C_VISIBLE >= 1999 || __BSD_VISIBLE
|
#if __ISO_C_VISIBLE >= 1999 || __BSD_VISIBLE
|
||||||
int snprintf(char * __restrict, size_t, const char * __restrict, ...)
|
int snprintf(char * __restrict, size_t, const char * __restrict, ...)
|
||||||
|
@ -58,7 +58,7 @@ extern int unsetenv(const char*);
|
|||||||
extern int clearenv(void);
|
extern int clearenv(void);
|
||||||
|
|
||||||
extern char* mkdtemp(char*);
|
extern char* mkdtemp(char*);
|
||||||
extern char* mktemp(char*) __warnattr("mktemp possibly used unsafely; consider using mkstemp");
|
extern char* mktemp(char*) __attribute__((deprecated("mktemp is unsafe, use mkstemp or tmpfile instead")));
|
||||||
|
|
||||||
extern int mkostemp64(char*, int);
|
extern int mkostemp64(char*, int);
|
||||||
extern int mkostemp(char*, int);
|
extern int mkostemp(char*, int);
|
||||||
|
@ -44,6 +44,9 @@ extern void* memchr(const void *, int, size_t) __purefunc;
|
|||||||
extern void* memrchr(const void *, int, size_t) __purefunc;
|
extern void* memrchr(const void *, int, size_t) __purefunc;
|
||||||
extern int memcmp(const void *, const void *, size_t) __purefunc;
|
extern int memcmp(const void *, const void *, size_t) __purefunc;
|
||||||
extern void* memcpy(void* __restrict, const void* __restrict, size_t);
|
extern void* memcpy(void* __restrict, const void* __restrict, size_t);
|
||||||
|
#if defined(__USE_GNU)
|
||||||
|
extern void* mempcpy(void* __restrict, const void* __restrict, size_t);
|
||||||
|
#endif
|
||||||
extern void* memmove(void *, const void *, size_t);
|
extern void* memmove(void *, const void *, size_t);
|
||||||
extern void* memset(void *, int, size_t);
|
extern void* memset(void *, int, size_t);
|
||||||
extern void* memmem(const void *, size_t, const void *, size_t) __purefunc;
|
extern void* memmem(const void *, size_t, const void *, size_t) __purefunc;
|
||||||
|
@ -335,13 +335,15 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __GNUC_PREREQ(4, 3)
|
#if __GNUC_PREREQ(4, 3)
|
||||||
#define __errordecl(name, msg) extern void name(void) __attribute__((__error__(msg)))
|
#define __errorattr(msg) __attribute__((__error__(msg)))
|
||||||
#define __warnattr(msg) __attribute__((__warning__(msg)))
|
#define __warnattr(msg) __attribute__((__warning__(msg)))
|
||||||
#else
|
#else
|
||||||
#define __errordecl(name, msg) extern void name(void)
|
#define __errorattr(msg)
|
||||||
#define __warnattr(msg)
|
#define __warnattr(msg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define __errordecl(name, msg) extern void name(void) __errorattr(msg)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some BSD source needs these macros.
|
* Some BSD source needs these macros.
|
||||||
* Originally they embedded the rcs versions of each source file
|
* Originally they embedded the rcs versions of each source file
|
||||||
@ -378,6 +380,15 @@
|
|||||||
# define __USE_BSD 1
|
# define __USE_BSD 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _FILE_OFFSET_BITS 64 support.
|
||||||
|
*/
|
||||||
|
#if !defined(__LP64__) && defined(_FILE_OFFSET_BITS)
|
||||||
|
#if _FILE_OFFSET_BITS == 64
|
||||||
|
#define __USE_FILE_OFFSET64 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* POSIX.1 requires that the macros we test be defined before any standard
|
* POSIX.1 requires that the macros we test be defined before any standard
|
||||||
* header file is included.
|
* header file is included.
|
||||||
|
@ -25,14 +25,19 @@
|
|||||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_FILE_H_
|
#ifndef _SYS_FILE_H_
|
||||||
#define _SYS_FILE_H_
|
#define _SYS_FILE_H_
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
/* ANDROID: needed for flock() */
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
int flock(int, int);
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
#endif /* _SYS_FILE_H_ */
|
#endif /* _SYS_FILE_H_ */
|
||||||
|
@ -49,8 +49,13 @@ __BEGIN_DECLS
|
|||||||
#define POSIX_MADV_WILLNEED MADV_WILLNEED
|
#define POSIX_MADV_WILLNEED MADV_WILLNEED
|
||||||
#define POSIX_MADV_DONTNEED MADV_DONTNEED
|
#define POSIX_MADV_DONTNEED MADV_DONTNEED
|
||||||
|
|
||||||
|
#if defined(__USE_FILE_OFFSET64)
|
||||||
|
extern void* mmap(void*, size_t, int, int, int, off_t) __RENAME(mmap64);
|
||||||
|
#else
|
||||||
extern void* mmap(void*, size_t, int, int, int, off_t);
|
extern void* mmap(void*, size_t, int, int, int, off_t);
|
||||||
|
#endif
|
||||||
extern void* mmap64(void*, size_t, int, int, int, off64_t);
|
extern void* mmap64(void*, size_t, int, int, int, off64_t);
|
||||||
|
|
||||||
extern int munmap(void*, size_t);
|
extern int munmap(void*, size_t);
|
||||||
extern int msync(const void*, size_t, int);
|
extern int msync(const void*, size_t, int);
|
||||||
extern int mprotect(const void*, size_t, int);
|
extern int mprotect(const void*, size_t, int);
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_MOUNT_H
|
#ifndef _SYS_MOUNT_H
|
||||||
#define _SYS_MOUNT_H
|
#define _SYS_MOUNT_H
|
||||||
|
|
||||||
@ -35,9 +36,10 @@
|
|||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
/* umount2 flags. */
|
/* umount2 flags. */
|
||||||
#define MNT_FORCE 1 /* Forcibly unmount */
|
#define MNT_FORCE 1
|
||||||
#define MNT_DETACH 2 /* Detach from tree only */
|
#define MNT_DETACH 2
|
||||||
#define MNT_EXPIRE 4 /* Mark for expiry */
|
#define MNT_EXPIRE 4
|
||||||
|
#define UMOUNT_NOFOLLOW 8
|
||||||
|
|
||||||
extern int mount(const char*, const char*, const char*, unsigned long, const void*);
|
extern int mount(const char*, const char*, const char*, unsigned long, const void*);
|
||||||
extern int umount(const char*);
|
extern int umount(const char*);
|
||||||
|
@ -36,6 +36,10 @@
|
|||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
/* The kernel header doesn't have these, but POSIX does. */
|
||||||
|
#define RLIM_SAVED_CUR RLIM_INFINITY
|
||||||
|
#define RLIM_SAVED_MAX RLIM_INFINITY
|
||||||
|
|
||||||
typedef unsigned long rlim_t;
|
typedef unsigned long rlim_t;
|
||||||
|
|
||||||
extern int getrlimit(int, struct rlimit*);
|
extern int getrlimit(int, struct rlimit*);
|
||||||
|
@ -34,7 +34,11 @@
|
|||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
#if defined(__USE_FILE_OFFSET64)
|
||||||
|
extern ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count) __RENAME(sendfile64);
|
||||||
|
#else
|
||||||
extern ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
|
extern ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
|
||||||
|
#endif
|
||||||
extern ssize_t sendfile64(int out_fd, int in_fd, off64_t* offset, size_t count);
|
extern ssize_t sendfile64(int out_fd, int in_fd, off64_t* offset, size_t count);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
@ -25,20 +25,13 @@
|
|||||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_SYSCALL_H_
|
#ifndef _SYS_SYSCALL_H_
|
||||||
#define _SYS_SYSCALL_H_
|
#define _SYS_SYSCALL_H_
|
||||||
|
|
||||||
#include <errno.h>
|
#include <asm/unistd.h> /* Linux kernel __NR_* names. */
|
||||||
#include <sys/cdefs.h>
|
#include <sys/glibc-syscalls.h> /* glibc-compatible SYS_* aliases. */
|
||||||
#include <sys/types.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
|
|
||||||
#include <sys/glibc-syscalls.h> /* glibc-compatible SYS_* aliases for our __NR_* names. */
|
/* The syscall function itself is declared in <unistd.h>, not here. */
|
||||||
|
|
||||||
__BEGIN_DECLS
|
|
||||||
|
|
||||||
long syscall(long number, ...);
|
|
||||||
|
|
||||||
__END_DECLS
|
|
||||||
|
|
||||||
#endif /* _SYS_SYSCALL_H_ */
|
#endif /* _SYS_SYSCALL_H_ */
|
||||||
|
@ -90,16 +90,14 @@ typedef uint64_t dev_t;
|
|||||||
typedef __kernel_time_t __time_t;
|
typedef __kernel_time_t __time_t;
|
||||||
typedef __time_t time_t;
|
typedef __time_t time_t;
|
||||||
|
|
||||||
/* This historical accident means that we had a 32-bit off_t on 32-bit architectures. */
|
#if defined(__USE_FILE_OFFSET64) || defined(__LP64__)
|
||||||
#if !defined(__LP64__)
|
typedef int64_t off_t;
|
||||||
typedef __kernel_off_t off_t;
|
typedef off_t loff_t;
|
||||||
typedef __kernel_loff_t loff_t;
|
|
||||||
typedef loff_t off64_t;
|
typedef loff_t off64_t;
|
||||||
#else
|
#else
|
||||||
/* We could re-use the LP32 definitions, but that would mean that although off_t and loff_t/off64_t
|
/* This historical accident means that we had a 32-bit off_t on 32-bit architectures. */
|
||||||
* would be the same size, they wouldn't actually be the same type, which can lead to warnings. */
|
|
||||||
typedef __kernel_off_t off_t;
|
typedef __kernel_off_t off_t;
|
||||||
typedef off_t loff_t;
|
typedef __kernel_loff_t loff_t;
|
||||||
typedef loff_t off64_t;
|
typedef loff_t off64_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ __BEGIN_DECLS
|
|||||||
|
|
||||||
#define LOG_PRIMASK 7
|
#define LOG_PRIMASK 7
|
||||||
#define LOG_PRI(x) ((x) & LOG_PRIMASK)
|
#define LOG_PRI(x) ((x) & LOG_PRIMASK)
|
||||||
|
#define LOG_MAKEPRI(fac, pri) ((fac) | (pri))
|
||||||
|
|
||||||
/* Facilities are currently ignored on Android. */
|
/* Facilities are currently ignored on Android. */
|
||||||
#define LOG_KERN 0000
|
#define LOG_KERN 0000
|
||||||
|
@ -116,10 +116,6 @@ extern int setresgid(gid_t, gid_t, gid_t);
|
|||||||
extern int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
|
extern int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
|
||||||
extern int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
|
extern int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
|
||||||
extern char* getlogin(void);
|
extern char* getlogin(void);
|
||||||
extern char* getusershell(void);
|
|
||||||
extern void setusershell(void);
|
|
||||||
extern void endusershell(void);
|
|
||||||
|
|
||||||
|
|
||||||
extern long fpathconf(int, int);
|
extern long fpathconf(int, int);
|
||||||
extern long pathconf(const char*, int);
|
extern long pathconf(const char*, int);
|
||||||
@ -146,32 +142,40 @@ extern int chown(const char *, uid_t, gid_t);
|
|||||||
extern int fchown(int, uid_t, gid_t);
|
extern int fchown(int, uid_t, gid_t);
|
||||||
extern int fchownat(int, const char*, uid_t, gid_t, int);
|
extern int fchownat(int, const char*, uid_t, gid_t, int);
|
||||||
extern int lchown(const char *, uid_t, gid_t);
|
extern int lchown(const char *, uid_t, gid_t);
|
||||||
extern int truncate(const char *, off_t);
|
|
||||||
extern int truncate64(const char *, off64_t);
|
|
||||||
extern char *getcwd(char *, size_t);
|
extern char *getcwd(char *, size_t);
|
||||||
|
|
||||||
extern int sync(void);
|
extern int sync(void);
|
||||||
|
|
||||||
extern int close(int);
|
extern int close(int);
|
||||||
extern off_t lseek(int, off_t, int);
|
|
||||||
extern off64_t lseek64(int, off64_t, int);
|
|
||||||
|
|
||||||
extern ssize_t read(int, void *, size_t);
|
extern ssize_t read(int, void *, size_t);
|
||||||
extern ssize_t write(int, const void *, size_t);
|
extern ssize_t write(int, const void *, size_t);
|
||||||
extern ssize_t pread(int, void *, size_t, off_t);
|
|
||||||
extern ssize_t pread64(int, void *, size_t, off64_t);
|
|
||||||
extern ssize_t pwrite(int, const void *, size_t, off_t);
|
|
||||||
extern ssize_t pwrite64(int, const void *, size_t, off64_t);
|
|
||||||
|
|
||||||
extern int dup(int);
|
extern int dup(int);
|
||||||
extern int dup2(int, int);
|
extern int dup2(int, int);
|
||||||
extern int dup3(int, int, int);
|
extern int dup3(int, int, int);
|
||||||
extern int fcntl(int, int, ...);
|
extern int fcntl(int, int, ...);
|
||||||
extern int ioctl(int, int, ...);
|
extern int ioctl(int, int, ...);
|
||||||
extern int flock(int, int);
|
|
||||||
extern int fsync(int);
|
extern int fsync(int);
|
||||||
extern int fdatasync(int);
|
extern int fdatasync(int);
|
||||||
|
|
||||||
|
#if defined(__USE_FILE_OFFSET64)
|
||||||
|
extern int truncate(const char *, off_t) __RENAME(truncate64);
|
||||||
|
extern off_t lseek(int, off_t, int) __RENAME(lseek64);
|
||||||
|
extern ssize_t pread(int, void *, size_t, off_t) __RENAME(pread64);
|
||||||
|
extern ssize_t pwrite(int, const void *, size_t, off_t) __RENAME(pwrite64);
|
||||||
|
extern int ftruncate(int, off_t) __RENAME(ftruncate64);
|
||||||
|
#else
|
||||||
|
extern int truncate(const char *, off_t);
|
||||||
|
extern off_t lseek(int, off_t, int);
|
||||||
|
extern ssize_t pread(int, void *, size_t, off_t);
|
||||||
|
extern ssize_t pwrite(int, const void *, size_t, off_t);
|
||||||
extern int ftruncate(int, off_t);
|
extern int ftruncate(int, off_t);
|
||||||
|
#endif
|
||||||
|
extern int truncate64(const char *, off64_t);
|
||||||
|
extern off64_t lseek64(int, off64_t, int);
|
||||||
|
extern ssize_t pread64(int, void *, size_t, off64_t);
|
||||||
|
extern ssize_t pwrite64(int, const void *, size_t, off64_t);
|
||||||
extern int ftruncate64(int, off64_t);
|
extern int ftruncate64(int, off64_t);
|
||||||
|
|
||||||
extern int pause(void);
|
extern int pause(void);
|
||||||
@ -200,6 +204,8 @@ int getpagesize(void);
|
|||||||
|
|
||||||
long sysconf(int);
|
long sysconf(int);
|
||||||
|
|
||||||
|
long syscall(long number, ...);
|
||||||
|
|
||||||
extern int daemon(int, int);
|
extern int daemon(int, int);
|
||||||
|
|
||||||
#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__))
|
#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__))
|
||||||
|
@ -151,6 +151,9 @@ extern int wcwidth(wchar_t);
|
|||||||
extern wchar_t *wmemchr(const wchar_t *, wchar_t, size_t);
|
extern wchar_t *wmemchr(const wchar_t *, wchar_t, size_t);
|
||||||
extern int wmemcmp(const wchar_t *, const wchar_t *, size_t);
|
extern int wmemcmp(const wchar_t *, const wchar_t *, size_t);
|
||||||
extern wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);
|
extern wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);
|
||||||
|
#if defined(__USE_GNU)
|
||||||
|
extern wchar_t *wmempcpy(wchar_t *, const wchar_t *, size_t);
|
||||||
|
#endif
|
||||||
extern wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);
|
extern wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);
|
||||||
extern wchar_t *wmemset(wchar_t *, wchar_t, size_t);
|
extern wchar_t *wmemset(wchar_t *, wchar_t, size_t);
|
||||||
extern int wprintf(const wchar_t *, ...);
|
extern int wprintf(const wchar_t *, ...);
|
||||||
|
@ -38,15 +38,17 @@
|
|||||||
|
|
||||||
// We used to use pthread_once to initialize the keys, but life is more predictable
|
// We used to use pthread_once to initialize the keys, but life is more predictable
|
||||||
// if we allocate them all up front when the C library starts up, via __constructor__.
|
// if we allocate them all up front when the C library starts up, via __constructor__.
|
||||||
|
#define BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR(key_name, key_destructor) \
|
||||||
|
static pthread_key_t key_name; \
|
||||||
|
__attribute__((constructor)) static void __bionic_tls_ ## key_name ## _key_init() { \
|
||||||
|
pthread_key_create(&key_name, key_destructor); \
|
||||||
|
}
|
||||||
|
|
||||||
#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \
|
#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \
|
||||||
static pthread_key_t __bionic_tls_ ## name ## _key; \
|
|
||||||
static void __bionic_tls_ ## name ## _key_destroy(void* buffer) { \
|
static void __bionic_tls_ ## name ## _key_destroy(void* buffer) { \
|
||||||
free(buffer); \
|
free(buffer); \
|
||||||
} \
|
} \
|
||||||
__attribute__((constructor)) static void __bionic_tls_ ## name ## _key_init() { \
|
BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR(__bionic_tls_ ## name ## _key, __bionic_tls_ ## name ## _key_destroy)
|
||||||
pthread_key_create(&__bionic_tls_ ## name ## _key, __bionic_tls_ ## name ## _key_destroy); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leaves "name_tls_buffer" and "name_tls_buffer_size" defined and initialized.
|
// Leaves "name_tls_buffer" and "name_tls_buffer_size" defined and initialized.
|
||||||
#define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \
|
#define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \
|
||||||
|
@ -57,4 +57,8 @@
|
|||||||
ENTRY(f); \
|
ENTRY(f); \
|
||||||
.hidden f \
|
.hidden f \
|
||||||
|
|
||||||
|
#define ALIAS_SYMBOL(alias, original) \
|
||||||
|
.globl alias; \
|
||||||
|
.equ alias, original
|
||||||
|
|
||||||
#endif /* _PRIVATE_BIONIC_ASM_H_ */
|
#endif /* _PRIVATE_BIONIC_ASM_H_ */
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#ifndef BIONIC_ATOMIC_ARM_H
|
|
||||||
#define BIONIC_ATOMIC_ARM_H
|
|
||||||
|
|
||||||
__ATOMIC_INLINE__ void __bionic_memory_barrier() {
|
|
||||||
__asm__ __volatile__ ( "dmb ish" : : : "memory" );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare-and-swap, without any explicit barriers. Note that this function
|
|
||||||
* returns 0 on success, and 1 on failure. The opposite convention is typically
|
|
||||||
* used on other platforms.
|
|
||||||
*/
|
|
||||||
__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t prev, status;
|
|
||||||
do {
|
|
||||||
__asm__ __volatile__ (
|
|
||||||
"ldrex %0, [%3]\n"
|
|
||||||
"mov %1, #0\n"
|
|
||||||
"teq %0, %4\n"
|
|
||||||
#ifdef __thumb2__
|
|
||||||
"it eq\n"
|
|
||||||
#endif
|
|
||||||
"strexeq %1, %5, [%3]"
|
|
||||||
: "=&r" (prev), "=&r" (status), "+m"(*ptr)
|
|
||||||
: "r" (ptr), "Ir" (old_value), "r" (new_value)
|
|
||||||
: "cc");
|
|
||||||
} while (__builtin_expect(status != 0, 0));
|
|
||||||
return prev != old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap, without any explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t prev, status;
|
|
||||||
do {
|
|
||||||
__asm__ __volatile__ (
|
|
||||||
"ldrex %0, [%3]\n"
|
|
||||||
"strex %1, %4, [%3]"
|
|
||||||
: "=&r" (prev), "=&r" (status), "+m" (*ptr)
|
|
||||||
: "r" (ptr), "r" (new_value)
|
|
||||||
: "cc");
|
|
||||||
} while (__builtin_expect(status != 0, 0));
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomic decrement, without explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
|
|
||||||
int32_t prev, tmp, status;
|
|
||||||
do {
|
|
||||||
__asm__ __volatile__ (
|
|
||||||
"ldrex %0, [%4]\n"
|
|
||||||
"sub %1, %0, #1\n"
|
|
||||||
"strex %2, %1, [%4]"
|
|
||||||
: "=&r" (prev), "=&r" (tmp), "=&r" (status), "+m"(*ptr)
|
|
||||||
: "r" (ptr)
|
|
||||||
: "cc");
|
|
||||||
} while (__builtin_expect(status != 0, 0));
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SYS_ATOMICS_ARM_H */
|
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#ifndef BIONIC_ATOMIC_AARCH64_H
|
|
||||||
#define BIONIC_ATOMIC_AARCH64_H
|
|
||||||
|
|
||||||
/* For ARMv8, we can use the 'dmb' instruction directly */
|
|
||||||
__ATOMIC_INLINE__ void __bionic_memory_barrier() {
|
|
||||||
__asm__ __volatile__ ( "dmb ish" : : : "memory" );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare-and-swap, without any explicit barriers. Note that this function
|
|
||||||
* returns 0 on success, and 1 on failure. The opposite convention is typically
|
|
||||||
* used on other platforms.
|
|
||||||
*/
|
|
||||||
__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t tmp, oldval;
|
|
||||||
__asm__ __volatile__ (
|
|
||||||
"// atomic_cmpxchg\n"
|
|
||||||
"1: ldxr %w1, [%3]\n"
|
|
||||||
" cmp %w1, %w4\n"
|
|
||||||
" b.ne 2f\n"
|
|
||||||
" stxr %w0, %w5, [%3]\n"
|
|
||||||
" cbnz %w0, 1b\n"
|
|
||||||
"2:"
|
|
||||||
: "=&r" (tmp), "=&r" (oldval), "+o"(*ptr)
|
|
||||||
: "r" (ptr), "Ir" (old_value), "r" (new_value)
|
|
||||||
: "cc", "memory");
|
|
||||||
return oldval != old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap, without any explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t prev, status;
|
|
||||||
__asm__ __volatile__ (
|
|
||||||
"// atomic_swap\n"
|
|
||||||
"1: ldxr %w0, [%3]\n"
|
|
||||||
" stxr %w1, %w4, [%3]\n"
|
|
||||||
" cbnz %w1, 1b\n"
|
|
||||||
: "=&r" (prev), "=&r" (status), "+o" (*ptr)
|
|
||||||
: "r" (ptr), "r" (new_value)
|
|
||||||
: "cc", "memory");
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomic decrement, without explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
|
|
||||||
int32_t prev, tmp, status;
|
|
||||||
__asm__ __volatile__ (
|
|
||||||
"1: ldxr %w0, [%4]\n"
|
|
||||||
" sub %w1, %w0, #1\n"
|
|
||||||
" stxr %w2, %w1, [%4]\n"
|
|
||||||
" cbnz %w2, 1b"
|
|
||||||
: "=&r" (prev), "=&r" (tmp), "=&r" (status), "+m"(*ptr)
|
|
||||||
: "r" (ptr)
|
|
||||||
: "cc", "memory");
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BIONIC_ATOMICS_AARCH64_H */
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#ifndef BIONIC_ATOMIC_GCC_BUILTIN_H
|
|
||||||
#define BIONIC_ATOMIC_GCC_BUILTIN_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This header file is used by default if we don't have optimized atomic
|
|
||||||
* routines for a given platform. See bionic_atomic_arm.h and
|
|
||||||
* bionic_atomic_x86.h for examples.
|
|
||||||
*
|
|
||||||
* Note that the GCC builtins include barriers that aren't present in
|
|
||||||
* the architecture-specific assembler versions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
__ATOMIC_INLINE__ void __bionic_memory_barrier() {
|
|
||||||
__sync_synchronize();
|
|
||||||
}
|
|
||||||
|
|
||||||
__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
/* We must return 0 on success. */
|
|
||||||
return __sync_val_compare_and_swap(ptr, old_value, new_value) != old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t old_value;
|
|
||||||
do {
|
|
||||||
old_value = *ptr;
|
|
||||||
} while (__sync_val_compare_and_swap(ptr, old_value, new_value) != old_value);
|
|
||||||
return old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
|
|
||||||
/* We must return the old value. */
|
|
||||||
return __sync_fetch_and_add(ptr, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BIONIC_ATOMIC_GCC_BUILTIN_H */
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BIONIC_ATOMIC_INLINE_H
|
|
||||||
#define BIONIC_ATOMIC_INLINE_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Inline declarations and macros for some special-purpose atomic
|
|
||||||
* operations. These are intended for rare circumstances where a
|
|
||||||
* memory barrier needs to be issued inline rather than as a function
|
|
||||||
* call.
|
|
||||||
*
|
|
||||||
* Macros defined in this header:
|
|
||||||
*
|
|
||||||
* void ANDROID_MEMBAR_FULL()
|
|
||||||
* Full memory barrier. Provides a compiler reordering barrier, and
|
|
||||||
* on SMP systems emits an appropriate instruction.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Define __ATOMIC_INLINE__ to control the inlining of all atomics
|
|
||||||
* functions declared here. For a slight performance boost, we want
|
|
||||||
* all of them to be always_inline
|
|
||||||
*/
|
|
||||||
#define __ATOMIC_INLINE__ static __inline__ __attribute__((always_inline))
|
|
||||||
|
|
||||||
#if defined(__arm__)
|
|
||||||
# include "bionic_atomic_arm.h"
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
# include "bionic_atomic_arm64.h"
|
|
||||||
#elif defined(__i386__)
|
|
||||||
# include "bionic_atomic_x86.h"
|
|
||||||
#elif defined(__mips__)
|
|
||||||
# include "bionic_atomic_mips.h"
|
|
||||||
#else
|
|
||||||
# include "bionic_atomic_gcc_builtin.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ANDROID_MEMBAR_FULL __bionic_memory_barrier
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // BIONIC_ATOMIC_INLINE_H
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#ifndef BIONIC_ATOMIC_MIPS_H
|
|
||||||
#define BIONIC_ATOMIC_MIPS_H
|
|
||||||
|
|
||||||
/* Define a full memory barrier, this is only needed if we build the
|
|
||||||
* platform for a multi-core device.
|
|
||||||
*/
|
|
||||||
|
|
||||||
__ATOMIC_INLINE__ void __bionic_memory_barrier() {
|
|
||||||
__asm__ __volatile__ ( "sync" : : : "memory" );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare-and-swap, without any explicit barriers. Note that this function
|
|
||||||
* returns 0 on success, and 1 on failure. The opposite convention is typically
|
|
||||||
* used on other platforms.
|
|
||||||
*/
|
|
||||||
__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t prev, status;
|
|
||||||
__asm__ __volatile__ ("1: move %[status], %[new_value] \n"
|
|
||||||
" ll %[prev], 0(%[ptr]) \n"
|
|
||||||
" bne %[old_value], %[prev], 2f \n"
|
|
||||||
" sc %[status], 0(%[ptr]) \n"
|
|
||||||
" beqz %[status], 1b \n"
|
|
||||||
"2: \n"
|
|
||||||
: [prev]"=&r"(prev), [status]"=&r"(status), "+m"(*ptr)
|
|
||||||
: [new_value]"r"(new_value), [old_value]"r"(old_value), [ptr]"r"(ptr)
|
|
||||||
: "memory");
|
|
||||||
return prev != old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap, without any explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t prev, status;
|
|
||||||
__asm__ __volatile__ ("1: move %[status], %[new_value] \n"
|
|
||||||
" ll %[prev], 0(%[ptr]) \n"
|
|
||||||
" sc %[status], 0(%[ptr]) \n"
|
|
||||||
" beqz %[status], 1b \n"
|
|
||||||
: [prev]"=&r"(prev), [status]"=&r"(status), "+m"(*ptr)
|
|
||||||
: [ptr]"r"(ptr), [new_value]"r"(new_value)
|
|
||||||
: "memory");
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomic decrement, without explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
|
|
||||||
int32_t prev, status;
|
|
||||||
__asm__ __volatile__ ("1: ll %[prev], 0(%[ptr]) \n"
|
|
||||||
" addiu %[status], %[prev], -1 \n"
|
|
||||||
" sc %[status], 0(%[ptr]) \n"
|
|
||||||
" beqz %[status], 1b \n"
|
|
||||||
: [prev]"=&r" (prev), [status]"=&r"(status), "+m" (*ptr)
|
|
||||||
: [ptr]"r"(ptr)
|
|
||||||
: "memory");
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BIONIC_ATOMIC_MIPS_H */
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#ifndef BIONIC_ATOMIC_X86_H
|
|
||||||
#define BIONIC_ATOMIC_X86_H
|
|
||||||
|
|
||||||
/* Define a full memory barrier, this is only needed if we build the
|
|
||||||
* platform for a multi-core device.
|
|
||||||
*/
|
|
||||||
__ATOMIC_INLINE__ void __bionic_memory_barrier() {
|
|
||||||
__asm__ __volatile__ ( "mfence" : : : "memory" );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare-and-swap, without any explicit barriers. Note that this function
|
|
||||||
* returns 0 on success, and 1 on failure. The opposite convention is typically
|
|
||||||
* used on other platforms.
|
|
||||||
*/
|
|
||||||
__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
|
|
||||||
int32_t prev;
|
|
||||||
__asm__ __volatile__ ("lock; cmpxchgl %1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "q" (new_value), "m" (*ptr), "0" (old_value)
|
|
||||||
: "memory");
|
|
||||||
return prev != old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap, without any explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t *ptr) {
|
|
||||||
__asm__ __volatile__ ("xchgl %1, %0"
|
|
||||||
: "=r" (new_value)
|
|
||||||
: "m" (*ptr), "0" (new_value)
|
|
||||||
: "memory");
|
|
||||||
return new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomic decrement, without explicit barriers. */
|
|
||||||
__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
|
|
||||||
int increment = -1;
|
|
||||||
__asm__ __volatile__ ("lock; xaddl %0, %1"
|
|
||||||
: "+r" (increment), "+m" (*ptr)
|
|
||||||
: : "memory");
|
|
||||||
/* increment now holds the old value of *ptr */
|
|
||||||
return increment;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BIONIC_ATOMIC_X86_H */
|
|
@ -34,6 +34,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -67,18 +67,18 @@ enum {
|
|||||||
TLS_SLOT_STACK_GUARD = 5, // GCC requires this specific slot for x86.
|
TLS_SLOT_STACK_GUARD = 5, // GCC requires this specific slot for x86.
|
||||||
TLS_SLOT_DLERROR,
|
TLS_SLOT_DLERROR,
|
||||||
|
|
||||||
TLS_SLOT_FIRST_USER_SLOT // Must come last!
|
BIONIC_TLS_SLOTS // Must come last!
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two kinds of slot used internally by bionic --- there are the well-known slots
|
* Bionic uses some pthread keys internally. All pthread keys used internally
|
||||||
* enumerated above, and then there are those that are allocated during startup by calls to
|
* should be created in constructors, except for keys that may be used in or before constructors.
|
||||||
* pthread_key_create; grep for GLOBAL_INIT_THREAD_LOCAL_BUFFER to find those. We need to manually
|
* We need to manually maintain the count of pthread keys used internally, but
|
||||||
* maintain that second number, but pthread_test will fail if we forget.
|
* pthread_test should fail if we forget.
|
||||||
* Following are current pthread keys used internally:
|
* Following are current pthread keys used internally by libc:
|
||||||
* basename libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* basename libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* dirname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* dirname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* uselocale libc
|
* uselocale libc (can be used in constructors)
|
||||||
* getmntent_mntent libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* getmntent_mntent libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* getmntent_strings libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* getmntent_strings libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* ptsname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* ptsname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
@ -87,29 +87,30 @@ enum {
|
|||||||
* strsignal libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* strsignal libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* passwd libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* passwd libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* group libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
* group libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER)
|
||||||
* _res_key libc
|
* _res_key libc (BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LIBC_PTHREAD_KEY_RESERVED_COUNT 12
|
||||||
|
|
||||||
|
#if defined(USE_JEMALLOC)
|
||||||
|
/* Following are current pthread keys used internally by jemalloc:
|
||||||
* je_thread_allocated_tsd jemalloc
|
* je_thread_allocated_tsd jemalloc
|
||||||
* je_arenas_tsd jemalloc
|
* je_arenas_tsd jemalloc
|
||||||
* je_tcache_tsd jemalloc
|
* je_tcache_tsd jemalloc
|
||||||
* je_tcache_enabled_tsd jemalloc
|
* je_tcache_enabled_tsd jemalloc
|
||||||
* je_quarantine_tsd jemalloc
|
* je_quarantine_tsd jemalloc
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 5
|
||||||
#define LIBC_TLS_RESERVED_SLOTS 12
|
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT (LIBC_PTHREAD_KEY_RESERVED_COUNT + JEMALLOC_PTHREAD_KEY_RESERVED_COUNT)
|
||||||
|
|
||||||
#if defined(USE_JEMALLOC)
|
|
||||||
/* jemalloc uses 5 keys for itself. */
|
|
||||||
#define BIONIC_TLS_RESERVED_SLOTS (LIBC_TLS_RESERVED_SLOTS + 5)
|
|
||||||
#else
|
#else
|
||||||
#define BIONIC_TLS_RESERVED_SLOTS LIBC_TLS_RESERVED_SLOTS
|
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT LIBC_PTHREAD_KEY_RESERVED_COUNT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum number of elements in the TLS array.
|
* Maximum number of pthread keys allocated.
|
||||||
* This includes space for pthread keys and our own internal slots.
|
* This includes pthread keys used internally and externally.
|
||||||
*/
|
*/
|
||||||
#define BIONIC_TLS_SLOTS (PTHREAD_KEYS_MAX + TLS_SLOT_FIRST_USER_SLOT + BIONIC_TLS_RESERVED_SLOTS)
|
#define BIONIC_PTHREAD_KEY_COUNT (BIONIC_PTHREAD_KEY_RESERVED_COUNT + PTHREAD_KEYS_MAX)
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
|
@ -102,6 +102,12 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
|
|||||||
* avoid copying it through the buffer?
|
* avoid copying it through the buffer?
|
||||||
*/
|
*/
|
||||||
if (total > (size_t) fp->_bf._size) {
|
if (total > (size_t) fp->_bf._size) {
|
||||||
|
/*
|
||||||
|
* Make sure that fseek doesn't think it can
|
||||||
|
* reuse the buffer since we are going to read
|
||||||
|
* directly from the file descriptor.
|
||||||
|
*/
|
||||||
|
fp->_flags |= __SMOD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
48
libc/upstream-openbsd/lib/libc/string/memchr.c
Normal file
48
libc/upstream-openbsd/lib/libc/string/memchr.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* $OpenBSD: memchr.c,v 1.7 2005/08/08 08:05:37 espie Exp $ */
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1990 The Regents of the University of California.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to Berkeley by
|
||||||
|
* Chris Torek.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void *
|
||||||
|
memchr(const void *s, int c, size_t n)
|
||||||
|
{
|
||||||
|
if (n != 0) {
|
||||||
|
const unsigned char *p = s;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (*p++ == (unsigned char)c)
|
||||||
|
return ((void *)(p - 1));
|
||||||
|
} while (--n != 0);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
38
libc/upstream-openbsd/lib/libc/string/memrchr.c
Normal file
38
libc/upstream-openbsd/lib/libc/string/memrchr.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* $OpenBSD: memrchr.c,v 1.2 2007/11/27 16:22:12 martynas Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reverse memchr()
|
||||||
|
* Find the last occurrence of 'c' in the buffer 's' of size 'n'.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
memrchr(const void *s, int c, size_t n)
|
||||||
|
{
|
||||||
|
const unsigned char *cp;
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
cp = (unsigned char *)s + n;
|
||||||
|
do {
|
||||||
|
if (*(--cp) == (unsigned char)c)
|
||||||
|
return((void *)cp);
|
||||||
|
} while (--n != 0);
|
||||||
|
}
|
||||||
|
return(NULL);
|
||||||
|
}
|
Binary file not shown.
276
libm/Android.mk
276
libm/Android.mk
@ -21,19 +21,14 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/bsdsrc/b_tgamma.c \
|
upstream-freebsd/lib/msun/bsdsrc/b_tgamma.c \
|
||||||
upstream-freebsd/lib/msun/src/catrig.c \
|
upstream-freebsd/lib/msun/src/catrig.c \
|
||||||
upstream-freebsd/lib/msun/src/catrigf.c \
|
upstream-freebsd/lib/msun/src/catrigf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_acos.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_acosf.c \
|
upstream-freebsd/lib/msun/src/e_acosf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_acosh.c \
|
upstream-freebsd/lib/msun/src/e_acosh.c \
|
||||||
upstream-freebsd/lib/msun/src/e_acoshf.c \
|
upstream-freebsd/lib/msun/src/e_acoshf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_asin.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_asinf.c \
|
upstream-freebsd/lib/msun/src/e_asinf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_atan2.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_atan2f.c \
|
upstream-freebsd/lib/msun/src/e_atan2f.c \
|
||||||
upstream-freebsd/lib/msun/src/e_atanh.c \
|
upstream-freebsd/lib/msun/src/e_atanh.c \
|
||||||
upstream-freebsd/lib/msun/src/e_atanhf.c \
|
upstream-freebsd/lib/msun/src/e_atanhf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_cosh.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_coshf.c \
|
upstream-freebsd/lib/msun/src/e_coshf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_exp.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_expf.c \
|
upstream-freebsd/lib/msun/src/e_expf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_fmod.c \
|
upstream-freebsd/lib/msun/src/e_fmod.c \
|
||||||
upstream-freebsd/lib/msun/src/e_fmodf.c \
|
upstream-freebsd/lib/msun/src/e_fmodf.c \
|
||||||
@ -41,7 +36,6 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/e_gammaf.c \
|
upstream-freebsd/lib/msun/src/e_gammaf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_gammaf_r.c \
|
upstream-freebsd/lib/msun/src/e_gammaf_r.c \
|
||||||
upstream-freebsd/lib/msun/src/e_gamma_r.c \
|
upstream-freebsd/lib/msun/src/e_gamma_r.c \
|
||||||
upstream-freebsd/lib/msun/src/e_hypot.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_hypotf.c \
|
upstream-freebsd/lib/msun/src/e_hypotf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_j0.c \
|
upstream-freebsd/lib/msun/src/e_j0.c \
|
||||||
upstream-freebsd/lib/msun/src/e_j0f.c \
|
upstream-freebsd/lib/msun/src/e_j0f.c \
|
||||||
@ -53,13 +47,10 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/e_lgammaf.c \
|
upstream-freebsd/lib/msun/src/e_lgammaf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_lgammaf_r.c \
|
upstream-freebsd/lib/msun/src/e_lgammaf_r.c \
|
||||||
upstream-freebsd/lib/msun/src/e_lgamma_r.c \
|
upstream-freebsd/lib/msun/src/e_lgamma_r.c \
|
||||||
upstream-freebsd/lib/msun/src/e_log10.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_log10f.c \
|
upstream-freebsd/lib/msun/src/e_log10f.c \
|
||||||
upstream-freebsd/lib/msun/src/e_log2.c \
|
upstream-freebsd/lib/msun/src/e_log2.c \
|
||||||
upstream-freebsd/lib/msun/src/e_log2f.c \
|
upstream-freebsd/lib/msun/src/e_log2f.c \
|
||||||
upstream-freebsd/lib/msun/src/e_log.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_logf.c \
|
upstream-freebsd/lib/msun/src/e_logf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_pow.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_powf.c \
|
upstream-freebsd/lib/msun/src/e_powf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_remainder.c \
|
upstream-freebsd/lib/msun/src/e_remainder.c \
|
||||||
upstream-freebsd/lib/msun/src/e_remainderf.c \
|
upstream-freebsd/lib/msun/src/e_remainderf.c \
|
||||||
@ -67,10 +58,7 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/e_rem_pio2f.c \
|
upstream-freebsd/lib/msun/src/e_rem_pio2f.c \
|
||||||
upstream-freebsd/lib/msun/src/e_scalb.c \
|
upstream-freebsd/lib/msun/src/e_scalb.c \
|
||||||
upstream-freebsd/lib/msun/src/e_scalbf.c \
|
upstream-freebsd/lib/msun/src/e_scalbf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_sinh.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_sinhf.c \
|
upstream-freebsd/lib/msun/src/e_sinhf.c \
|
||||||
upstream-freebsd/lib/msun/src/e_sqrt.c \
|
|
||||||
upstream-freebsd/lib/msun/src/e_sqrtf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/imprecise.c \
|
upstream-freebsd/lib/msun/src/imprecise.c \
|
||||||
upstream-freebsd/lib/msun/src/k_cos.c \
|
upstream-freebsd/lib/msun/src/k_cos.c \
|
||||||
upstream-freebsd/lib/msun/src/k_cosf.c \
|
upstream-freebsd/lib/msun/src/k_cosf.c \
|
||||||
@ -83,17 +71,13 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/k_tanf.c \
|
upstream-freebsd/lib/msun/src/k_tanf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_asinh.c \
|
upstream-freebsd/lib/msun/src/s_asinh.c \
|
||||||
upstream-freebsd/lib/msun/src/s_asinhf.c \
|
upstream-freebsd/lib/msun/src/s_asinhf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_atan.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_atanf.c \
|
upstream-freebsd/lib/msun/src/s_atanf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_carg.c \
|
upstream-freebsd/lib/msun/src/s_carg.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cargf.c \
|
upstream-freebsd/lib/msun/src/s_cargf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cargl.c \
|
upstream-freebsd/lib/msun/src/s_cargl.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cbrt.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_cbrtf.c \
|
upstream-freebsd/lib/msun/src/s_cbrtf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_ccosh.c \
|
upstream-freebsd/lib/msun/src/s_ccosh.c \
|
||||||
upstream-freebsd/lib/msun/src/s_ccoshf.c \
|
upstream-freebsd/lib/msun/src/s_ccoshf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_ceil.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_ceilf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_cexp.c \
|
upstream-freebsd/lib/msun/src/s_cexp.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cexpf.c \
|
upstream-freebsd/lib/msun/src/s_cexpf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cimag.c \
|
upstream-freebsd/lib/msun/src/s_cimag.c \
|
||||||
@ -104,7 +88,6 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/s_conjl.c \
|
upstream-freebsd/lib/msun/src/s_conjl.c \
|
||||||
upstream-freebsd/lib/msun/src/s_copysign.c \
|
upstream-freebsd/lib/msun/src/s_copysign.c \
|
||||||
upstream-freebsd/lib/msun/src/s_copysignf.c \
|
upstream-freebsd/lib/msun/src/s_copysignf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cos.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_cosf.c \
|
upstream-freebsd/lib/msun/src/s_cosf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cproj.c \
|
upstream-freebsd/lib/msun/src/s_cproj.c \
|
||||||
upstream-freebsd/lib/msun/src/s_cprojf.c \
|
upstream-freebsd/lib/msun/src/s_cprojf.c \
|
||||||
@ -123,17 +106,12 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/s_erff.c \
|
upstream-freebsd/lib/msun/src/s_erff.c \
|
||||||
upstream-freebsd/lib/msun/src/s_exp2.c \
|
upstream-freebsd/lib/msun/src/s_exp2.c \
|
||||||
upstream-freebsd/lib/msun/src/s_exp2f.c \
|
upstream-freebsd/lib/msun/src/s_exp2f.c \
|
||||||
upstream-freebsd/lib/msun/src/s_expm1.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_expm1f.c \
|
upstream-freebsd/lib/msun/src/s_expm1f.c \
|
||||||
upstream-freebsd/lib/msun/src/s_fabs.c \
|
upstream-freebsd/lib/msun/src/s_fabs.c \
|
||||||
upstream-freebsd/lib/msun/src/s_fabsf.c \
|
upstream-freebsd/lib/msun/src/s_fabsf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_fdim.c \
|
upstream-freebsd/lib/msun/src/s_fdim.c \
|
||||||
upstream-freebsd/lib/msun/src/s_finite.c \
|
upstream-freebsd/lib/msun/src/s_finite.c \
|
||||||
upstream-freebsd/lib/msun/src/s_finitef.c \
|
upstream-freebsd/lib/msun/src/s_finitef.c \
|
||||||
upstream-freebsd/lib/msun/src/s_floor.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_floorf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_fma.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_fmaf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_fmax.c \
|
upstream-freebsd/lib/msun/src/s_fmax.c \
|
||||||
upstream-freebsd/lib/msun/src/s_fmaxf.c \
|
upstream-freebsd/lib/msun/src/s_fmaxf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_fmin.c \
|
upstream-freebsd/lib/msun/src/s_fmin.c \
|
||||||
@ -142,16 +120,11 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/s_frexpf.c \
|
upstream-freebsd/lib/msun/src/s_frexpf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_ilogb.c \
|
upstream-freebsd/lib/msun/src/s_ilogb.c \
|
||||||
upstream-freebsd/lib/msun/src/s_ilogbf.c \
|
upstream-freebsd/lib/msun/src/s_ilogbf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_llrint.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_llrintf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_llround.c \
|
upstream-freebsd/lib/msun/src/s_llround.c \
|
||||||
upstream-freebsd/lib/msun/src/s_llroundf.c \
|
upstream-freebsd/lib/msun/src/s_llroundf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_log1p.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_log1pf.c \
|
upstream-freebsd/lib/msun/src/s_log1pf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_logb.c \
|
upstream-freebsd/lib/msun/src/s_logb.c \
|
||||||
upstream-freebsd/lib/msun/src/s_logbf.c \
|
upstream-freebsd/lib/msun/src/s_logbf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_lrint.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_lrintf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_lround.c \
|
upstream-freebsd/lib/msun/src/s_lround.c \
|
||||||
upstream-freebsd/lib/msun/src/s_lroundf.c \
|
upstream-freebsd/lib/msun/src/s_lroundf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_modf.c \
|
upstream-freebsd/lib/msun/src/s_modf.c \
|
||||||
@ -162,8 +135,6 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/s_nextafterf.c \
|
upstream-freebsd/lib/msun/src/s_nextafterf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_remquo.c \
|
upstream-freebsd/lib/msun/src/s_remquo.c \
|
||||||
upstream-freebsd/lib/msun/src/s_remquof.c \
|
upstream-freebsd/lib/msun/src/s_remquof.c \
|
||||||
upstream-freebsd/lib/msun/src/s_rint.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_rintf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_round.c \
|
upstream-freebsd/lib/msun/src/s_round.c \
|
||||||
upstream-freebsd/lib/msun/src/s_roundf.c \
|
upstream-freebsd/lib/msun/src/s_roundf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_scalbln.c \
|
upstream-freebsd/lib/msun/src/s_scalbln.c \
|
||||||
@ -172,15 +143,10 @@ LOCAL_SRC_FILES := \
|
|||||||
upstream-freebsd/lib/msun/src/s_signgam.c \
|
upstream-freebsd/lib/msun/src/s_signgam.c \
|
||||||
upstream-freebsd/lib/msun/src/s_significand.c \
|
upstream-freebsd/lib/msun/src/s_significand.c \
|
||||||
upstream-freebsd/lib/msun/src/s_significandf.c \
|
upstream-freebsd/lib/msun/src/s_significandf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_sin.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_sinf.c \
|
upstream-freebsd/lib/msun/src/s_sinf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_tan.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_tanf.c \
|
upstream-freebsd/lib/msun/src/s_tanf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_tanh.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_tanhf.c \
|
upstream-freebsd/lib/msun/src/s_tanhf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_tgammaf.c \
|
upstream-freebsd/lib/msun/src/s_tgammaf.c \
|
||||||
upstream-freebsd/lib/msun/src/s_trunc.c \
|
|
||||||
upstream-freebsd/lib/msun/src/s_truncf.c \
|
|
||||||
upstream-freebsd/lib/msun/src/w_cabs.c \
|
upstream-freebsd/lib/msun/src/w_cabs.c \
|
||||||
upstream-freebsd/lib/msun/src/w_cabsf.c \
|
upstream-freebsd/lib/msun/src/w_cabsf.c \
|
||||||
upstream-freebsd/lib/msun/src/w_cabsl.c \
|
upstream-freebsd/lib/msun/src/w_cabsl.c \
|
||||||
@ -261,23 +227,251 @@ LOCAL_SRC_FILES += \
|
|||||||
LOCAL_SRC_FILES += \
|
LOCAL_SRC_FILES += \
|
||||||
signbit.c \
|
signbit.c \
|
||||||
|
|
||||||
|
# Arch specific optimizations.
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# arm
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
LOCAL_SRC_FILES_arm += \
|
LOCAL_SRC_FILES_arm += \
|
||||||
arm/fenv.c \
|
arm/fenv.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_acos.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_asin.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_atan2.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_cosh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_exp.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_hypot.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_log.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_log10.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_pow.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sinh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_atan.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_cbrt.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceil.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceilf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_cos.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fma.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fmaf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floorf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_expm1.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_log1p.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_sin.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_tan.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_tanh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_trunc.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_truncf.c \
|
||||||
|
|
||||||
|
# s_floor.S requires neon instructions.
|
||||||
|
ifdef TARGET_2ND_ARCH
|
||||||
|
arch_variant := $(TARGET_2ND_ARCH_VARIANT)
|
||||||
|
else
|
||||||
|
arch_variant := $(TARGET_ARCH_VARIANT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Use the C version on armv7-a since it doesn't support neon instructions.
|
||||||
|
ifeq ($(arch_variant),armv7-a)
|
||||||
|
LOCAL_SRC_FILES_arm += \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sqrt.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sqrtf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floor.c \
|
||||||
|
|
||||||
|
else
|
||||||
|
LOCAL_SRC_FILES_arm += \
|
||||||
|
arm/e_sqrt.S \
|
||||||
|
arm/e_sqrtf.S \
|
||||||
|
arm/s_floor.S \
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# arm64
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
LOCAL_SRC_FILES_arm64 += \
|
LOCAL_SRC_FILES_arm64 += \
|
||||||
|
arm64/ceil.S \
|
||||||
arm64/fenv.c \
|
arm64/fenv.c \
|
||||||
|
arm64/fma.S \
|
||||||
|
arm64/floor.S \
|
||||||
|
arm64/lrint.S \
|
||||||
|
arm64/rint.S \
|
||||||
|
arm64/sqrt.S \
|
||||||
|
arm64/trunc.S \
|
||||||
|
upstream-freebsd/lib/msun/src/e_acos.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_asin.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_atan2.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_cosh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_exp.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_hypot.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_log.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_log10.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_pow.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sinh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_atan.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_cbrt.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_cos.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_expm1.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_log1p.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_sin.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_tan.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_tanh.c \
|
||||||
|
|
||||||
LOCAL_SRC_FILES_mips += \
|
# -----------------------------------------------------------------------------
|
||||||
|
# mips
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
libm_mips_arch_files := \
|
||||||
mips/fenv.c \
|
mips/fenv.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_acos.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_asin.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_atan2.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_cosh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_exp.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_hypot.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_log.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_log10.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_pow.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sinh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sqrt.c \
|
||||||
|
upstream-freebsd/lib/msun/src/e_sqrtf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_atan.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_cbrt.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceil.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceilf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_cos.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fma.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fmaf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floor.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floorf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_expm1.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_log1p.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_sin.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_tan.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_tanh.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_trunc.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_truncf.c \
|
||||||
|
|
||||||
LOCAL_SRC_FILES_mips64 += \
|
LOCAL_SRC_FILES_mips += $(libm_mips_arch_files)
|
||||||
mips/fenv.c \
|
LOCAL_SRC_FILES_mips64 += $(libm_mips_arch_files)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# x86
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
LOCAL_SRC_FILES_x86 += \
|
LOCAL_SRC_FILES_x86 += \
|
||||||
i387/fenv.c \
|
i387/fenv.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fma.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fmaf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rintf.c \
|
||||||
|
x86/sqrt.S \
|
||||||
|
x86/sqrtf.S \
|
||||||
|
x86/e_acos.S \
|
||||||
|
x86/e_asin.S \
|
||||||
|
x86/e_atan2.S \
|
||||||
|
x86/e_cosh.S \
|
||||||
|
x86/e_exp.S \
|
||||||
|
x86/e_hypot.S \
|
||||||
|
x86/e_log10.S \
|
||||||
|
x86/e_log.S \
|
||||||
|
x86/e_pow.S \
|
||||||
|
x86/e_sinh.S \
|
||||||
|
x86/libm_reduce_pi04l.S \
|
||||||
|
x86/libm_sincos_huge.S \
|
||||||
|
x86/libm_tancot_huge.S \
|
||||||
|
x86/s_atan.S \
|
||||||
|
x86/s_cbrt.S \
|
||||||
|
x86/s_cos.S \
|
||||||
|
x86/s_expm1.S \
|
||||||
|
x86/s_log1p.S \
|
||||||
|
x86/s_sin.S \
|
||||||
|
x86/s_tanh.S \
|
||||||
|
x86/s_tan.S \
|
||||||
|
|
||||||
|
ifeq ($(ARCH_X86_HAVE_SSE4_1),true)
|
||||||
|
LOCAL_SRC_FILES_x86 += \
|
||||||
|
x86/ceil.S \
|
||||||
|
x86/ceilf.S \
|
||||||
|
x86/floor.S \
|
||||||
|
x86/floorf.S \
|
||||||
|
x86/trunc.S \
|
||||||
|
x86/truncf.S \
|
||||||
|
|
||||||
|
else
|
||||||
|
LOCAL_SRC_FILES_x86 += \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceil.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceilf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floor.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floorf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_trunc.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_truncf.c \
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# x86_64
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
LOCAL_SRC_FILES_x86_64 += \
|
LOCAL_SRC_FILES_x86_64 += \
|
||||||
amd64/fenv.c \
|
amd64/fenv.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fma.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_fmaf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_llrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_lrintf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rint.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_rintf.c \
|
||||||
|
x86_64/sqrt.S \
|
||||||
|
x86_64/sqrtf.S \
|
||||||
|
x86_64/e_acos.S \
|
||||||
|
x86_64/e_asin.S \
|
||||||
|
x86_64/e_atan2.S \
|
||||||
|
x86_64/e_cosh.S \
|
||||||
|
x86_64/e_exp.S \
|
||||||
|
x86_64/e_hypot.S \
|
||||||
|
x86_64/e_log10.S \
|
||||||
|
x86_64/e_log.S \
|
||||||
|
x86_64/e_pow.S \
|
||||||
|
x86_64/e_sinh.S \
|
||||||
|
x86_64/s_atan.S \
|
||||||
|
x86_64/s_cbrt.S \
|
||||||
|
x86_64/s_cos.S \
|
||||||
|
x86_64/s_expm1.S \
|
||||||
|
x86_64/s_log1p.S \
|
||||||
|
x86_64/s_sin.S \
|
||||||
|
x86_64/s_tanh.S \
|
||||||
|
x86_64/s_tan.S \
|
||||||
|
|
||||||
|
ifeq ($(ARCH_X86_HAVE_SSE4_1),true)
|
||||||
|
LOCAL_SRC_FILES_x86_64 += \
|
||||||
|
x86_64/ceil.S \
|
||||||
|
x86_64/ceilf.S \
|
||||||
|
x86_64/floor.S \
|
||||||
|
x86_64/floorf.S \
|
||||||
|
x86_64/trunc.S \
|
||||||
|
x86_64/truncf.S \
|
||||||
|
|
||||||
|
else
|
||||||
|
LOCAL_SRC_FILES_x86_64 += \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceil.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_ceilf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floor.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_floorf.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_trunc.c \
|
||||||
|
upstream-freebsd/lib/msun/src/s_truncf.c \
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
LOCAL_C_INCLUDES_x86 += $(LOCAL_PATH)/i387
|
LOCAL_C_INCLUDES_x86 += $(LOCAL_PATH)/i387
|
||||||
|
|
||||||
@ -285,7 +479,6 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-freebsd/lib/msun/src/
|
|||||||
LOCAL_C_INCLUDES_64 += $(LOCAL_PATH)/upstream-freebsd/lib/msun/ld128/
|
LOCAL_C_INCLUDES_64 += $(LOCAL_PATH)/upstream-freebsd/lib/msun/ld128/
|
||||||
|
|
||||||
LOCAL_CLANG := $(libm_clang)
|
LOCAL_CLANG := $(libm_clang)
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
|
|
||||||
LOCAL_ARM_MODE := arm
|
LOCAL_ARM_MODE := arm
|
||||||
LOCAL_CFLAGS := \
|
LOCAL_CFLAGS := \
|
||||||
-DFLT_EVAL_METHOD=0 \
|
-DFLT_EVAL_METHOD=0 \
|
||||||
@ -297,6 +490,9 @@ LOCAL_CFLAGS := \
|
|||||||
-Wno-unknown-pragmas \
|
-Wno-unknown-pragmas \
|
||||||
-fvisibility=hidden \
|
-fvisibility=hidden \
|
||||||
|
|
||||||
|
LOCAL_ASFLAGS := \
|
||||||
|
-Ibionic/libc \
|
||||||
|
|
||||||
# Workaround the GCC "(long)fn -> lfn" optimization bug which will result in
|
# Workaround the GCC "(long)fn -> lfn" optimization bug which will result in
|
||||||
# self recursions for lrint, lrintf, and lrintl.
|
# self recursions for lrint, lrintf, and lrintl.
|
||||||
# BUG: 14225968
|
# BUG: 14225968
|
||||||
@ -317,9 +513,11 @@ include $(BUILD_STATIC_LIBRARY)
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
# TODO: This is to work around b/19059885. Remove after root cause is fixed
|
||||||
|
LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv
|
||||||
|
|
||||||
LOCAL_MODULE := libm
|
LOCAL_MODULE := libm
|
||||||
LOCAL_CLANG := $(libm_clang)
|
LOCAL_CLANG := $(libm_clang)
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
|
|
||||||
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
|
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
|
||||||
LOCAL_WHOLE_STATIC_LIBRARIES := libm
|
LOCAL_WHOLE_STATIC_LIBRARIES := libm
|
||||||
|
|
||||||
|
42
libm/arm/e_sqrt.S
Normal file
42
libm/arm/e_sqrt.S
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2014, NVIDIA Corporation. All rights reserved.
|
||||||
|
* Johnny Qiu <joqiu@nvidia.com>
|
||||||
|
* Shu Zhang <chazhang@nvidia.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
* * Neither the name of The Linux Foundation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||||
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(sqrt)
|
||||||
|
vmov.f64 d0, r0, r1
|
||||||
|
vsqrt.f64 d0, d0
|
||||||
|
vmov.f64 r0, r1, d0
|
||||||
|
bx lr
|
||||||
|
END(sqrt)
|
||||||
|
|
||||||
|
ALIAS_SYMBOL(sqrtl, sqrt);
|
39
libm/arm/e_sqrtf.S
Normal file
39
libm/arm/e_sqrtf.S
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2014, NVIDIA Corporation. All rights reserved.
|
||||||
|
* Johhnny Qiu <joqiu@nvidia.com>
|
||||||
|
* Shu Zhang <chazhang@nvidia.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
* * Neither the name of The Linux Foundation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||||
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(sqrtf)
|
||||||
|
vmov.f32 s0, r0
|
||||||
|
vsqrt.f32 s0, s0
|
||||||
|
vmov.f32 r0, s0
|
||||||
|
bx lr
|
||||||
|
END(sqrtf)
|
139
libm/arm/s_floor.S
Normal file
139
libm/arm/s_floor.S
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2014, NVIDIA Corporation. All rights reserved.
|
||||||
|
* Johnny Qiu <joqiu@nvidia.com>
|
||||||
|
* Shu Zhang <chazhang@nvidia.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
* * Neither the name of The Linux Foundation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||||
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(floor) /* x in r0, r1 */
|
||||||
|
|
||||||
|
and r3, r1, #0x80000000 /* sign(x) */
|
||||||
|
bic r1, r1, #0x80000000 /* x = abs(x) */
|
||||||
|
|
||||||
|
/* extract exp of x */
|
||||||
|
lsr r2, r1, #20
|
||||||
|
sub r2, r2, #0x3fc
|
||||||
|
subs r2, r2, #0x3 /* r2 <- exp */
|
||||||
|
|
||||||
|
/* |x| < 1.0? */
|
||||||
|
blt .Lx_lt_one
|
||||||
|
|
||||||
|
/* x < 0? */
|
||||||
|
cmp r3, #0
|
||||||
|
bne .Lclr_frac_neg
|
||||||
|
|
||||||
|
/* |x| <= 2^20? */
|
||||||
|
cmp r2, #20
|
||||||
|
ble .Lclr_frac_r1
|
||||||
|
|
||||||
|
/* |x| < 2^52? */
|
||||||
|
cmp r2, #52
|
||||||
|
blt .Lclr_frac_r0
|
||||||
|
|
||||||
|
/* return x */
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.Lclr_frac_r1:
|
||||||
|
rsb r2, r2, #20
|
||||||
|
lsr r1, r1, r2
|
||||||
|
lsl r1, r1, r2
|
||||||
|
mov r0, #0
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.Lclr_frac_r0:
|
||||||
|
rsb r2, r2, #52
|
||||||
|
lsr r0, r0, r2
|
||||||
|
lsl r0, r0, r2
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.Lclr_frac_neg:
|
||||||
|
/* |x| <= 2^20? */
|
||||||
|
cmp r2, #20
|
||||||
|
ble .Lclr_frac_r1_neg
|
||||||
|
|
||||||
|
/* |x| < 2^52? */
|
||||||
|
cmp r2, #52
|
||||||
|
blt .Lclr_frac_r0_neg
|
||||||
|
|
||||||
|
/* return x */
|
||||||
|
orr r1, r1, #0x80000000
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.Lclr_frac_r1_neg:
|
||||||
|
rsb r2, r2, #20
|
||||||
|
mov r3, #1
|
||||||
|
lsl r3, r3, r2
|
||||||
|
sub r3, r3, #1
|
||||||
|
and r3, r1, r3
|
||||||
|
orr r3, r3, r0
|
||||||
|
lsr r1, r1, r2
|
||||||
|
lsl r1, r1, r2
|
||||||
|
mov r0, #0
|
||||||
|
b .Lreturn_x_neg
|
||||||
|
|
||||||
|
.Lclr_frac_r0_neg:
|
||||||
|
rsb r2, r2, #52
|
||||||
|
mov r3, #1
|
||||||
|
lsl r3, r3, r2
|
||||||
|
sub r3, r3, #1
|
||||||
|
and r3, r0, r3
|
||||||
|
lsr r0, r0, r2
|
||||||
|
lsl r0, r0, r2
|
||||||
|
b .Lreturn_x_neg
|
||||||
|
|
||||||
|
.Lx_lt_one:
|
||||||
|
/* x == +-0? */
|
||||||
|
cmp r0, #0
|
||||||
|
cmpeq r1, #0
|
||||||
|
orreq r1, r1, r3
|
||||||
|
bxeq lr
|
||||||
|
|
||||||
|
/* (x > 0) ? 0 : -1 */
|
||||||
|
mov r1, #0x00100000
|
||||||
|
mov r0, #0
|
||||||
|
cmp r3, #0
|
||||||
|
movne r1, #0xc0000000
|
||||||
|
sub r1, r1, #0x00100000
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.Lreturn_x_neg:
|
||||||
|
cmp r3, #0
|
||||||
|
orr r1, r1, #0x80000000
|
||||||
|
bxeq lr
|
||||||
|
|
||||||
|
vmov d16, r0, r1
|
||||||
|
vmov.f64 d18, #1.0
|
||||||
|
vsub.f64 d16, d16, d18
|
||||||
|
vmov r0, r1, d16
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
END(floor)
|
||||||
|
|
||||||
|
ALIAS_SYMBOL(floorl, floor);
|
27
libm/arm64/ceil.S
Normal file
27
libm/arm64/ceil.S
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(ceil)
|
||||||
|
frintP d0, d0
|
||||||
|
ret
|
||||||
|
END(ceil)
|
||||||
|
|
||||||
|
ENTRY(ceilf)
|
||||||
|
frintP s0, s0
|
||||||
|
ret
|
||||||
|
END(ceilf)
|
27
libm/arm64/floor.S
Normal file
27
libm/arm64/floor.S
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(floor)
|
||||||
|
frintM d0, d0
|
||||||
|
ret
|
||||||
|
END(floor)
|
||||||
|
|
||||||
|
ENTRY(floorf)
|
||||||
|
frintM s0, s0
|
||||||
|
ret
|
||||||
|
END(floorf)
|
27
libm/arm64/fma.S
Normal file
27
libm/arm64/fma.S
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(fma)
|
||||||
|
fmadd d0, d0, d1, d2
|
||||||
|
ret
|
||||||
|
END(fma)
|
||||||
|
|
||||||
|
ENTRY(fmaf)
|
||||||
|
fmadd s0, s0, s1, s2
|
||||||
|
ret
|
||||||
|
END(fmaf)
|
34
libm/arm64/lrint.S
Normal file
34
libm/arm64/lrint.S
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(lrint)
|
||||||
|
frintX d0, d0
|
||||||
|
fcvtzs x0, d0
|
||||||
|
ret
|
||||||
|
END(lrint)
|
||||||
|
|
||||||
|
ENTRY(lrintf)
|
||||||
|
frintX s0, s0
|
||||||
|
fcvtzs x0, s0
|
||||||
|
ret
|
||||||
|
END(lrintf)
|
||||||
|
|
||||||
|
// sizeof(long) and sizeof(long long) are the same for aarch64
|
||||||
|
ALIAS_SYMBOL(llrint, lrint);
|
||||||
|
|
||||||
|
ALIAS_SYMBOL(llrintf, lrintf);
|
27
libm/arm64/rint.S
Normal file
27
libm/arm64/rint.S
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(rint)
|
||||||
|
frintX d0, d0
|
||||||
|
ret
|
||||||
|
END(rint)
|
||||||
|
|
||||||
|
ENTRY(rintf)
|
||||||
|
frintX s0, s0
|
||||||
|
ret
|
||||||
|
END(rintf)
|
27
libm/arm64/sqrt.S
Normal file
27
libm/arm64/sqrt.S
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(sqrt)
|
||||||
|
fsqrt d0, d0
|
||||||
|
ret
|
||||||
|
END(sqrt)
|
||||||
|
|
||||||
|
ENTRY(sqrtf)
|
||||||
|
fsqrt s0, s0
|
||||||
|
ret
|
||||||
|
END(sqrtf)
|
27
libm/arm64/trunc.S
Normal file
27
libm/arm64/trunc.S
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(trunc)
|
||||||
|
frintZ d0, d0
|
||||||
|
ret
|
||||||
|
END(trunc)
|
||||||
|
|
||||||
|
ENTRY(truncf)
|
||||||
|
frintZ s0, s0
|
||||||
|
ret
|
||||||
|
END(truncf)
|
43
libm/x86/ceil.S
Normal file
43
libm/x86/ceil.S
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2014, Intel Corporation
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Intel Corporation nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(ceil)
|
||||||
|
mov %esp,%eax
|
||||||
|
and $0xfffffff8,%eax
|
||||||
|
movsd 0x4(%esp),%xmm0
|
||||||
|
roundsd $0x2,%xmm0,%xmm0
|
||||||
|
movlpd %xmm0,-0x8(%eax)
|
||||||
|
fldl -0x8(%eax)
|
||||||
|
ret
|
||||||
|
END(ceil)
|
||||||
|
|
||||||
|
ALIAS_SYMBOL(ceill, ceil);
|
39
libm/x86/ceilf.S
Normal file
39
libm/x86/ceilf.S
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2014, Intel Corporation
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Intel Corporation nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
|
ENTRY(ceilf)
|
||||||
|
movss 0x4(%esp),%xmm0
|
||||||
|
roundss $0x2,%xmm0,%xmm0
|
||||||
|
movss %xmm0,-0x4(%esp)
|
||||||
|
flds -0x4(%esp)
|
||||||
|
ret
|
||||||
|
END(ceilf)
|
1929
libm/x86/e_acos.S
Normal file
1929
libm/x86/e_acos.S
Normal file
File diff suppressed because it is too large
Load Diff
2003
libm/x86/e_asin.S
Normal file
2003
libm/x86/e_asin.S
Normal file
File diff suppressed because it is too large
Load Diff
1221
libm/x86/e_atan2.S
Normal file
1221
libm/x86/e_atan2.S
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user