<commit_before>//  values.cpp : tests on values in scientific notation (sign, scale, fraction)
//
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.

#include "common.hpp"
#define POSIT_VERBOSE_OUTPUT
#define POSIT_TRACE_CONVERSION
#include <posit>

//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Constants
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#define DBL_DECIMAL_DIG  17                      // # of decimal digits of rounding precision
#define DBL_DIG          15                      // # of decimal digits of precision
#define DBL_EPSILON      2.2204460492503131e-016 // smallest such that 1.0+DBL_EPSILON != 1.0
#define DBL_HAS_SUBNORM  1                       // type does support subnormal numbers
#define DBL_MANT_DIG     53                      // # of bits in mantissa
#define DBL_MAX          1.7976931348623158e+308 // max value
#define DBL_MAX_10_EXP   308                     // max decimal exponent
#define DBL_MAX_EXP      1024                    // max binary exponent
#define DBL_MIN          2.2250738585072014e-308 // min positive value
#define DBL_MIN_10_EXP   (-307)                  // min decimal exponent
#define DBL_MIN_EXP      (-1021)                 // min binary exponent
#define _DBL_RADIX       2                       // exponent radix
#define DBL_TRUE_MIN     4.9406564584124654e-324 // min positive value

#define FLT_DECIMAL_DIG  9                       // # of decimal digits of rounding precision
#define FLT_DIG          6                       // # of decimal digits of precision
#define FLT_EPSILON      1.192092896e-07F        // smallest such that 1.0+FLT_EPSILON != 1.0
#define FLT_HAS_SUBNORM  1                       // type does support subnormal numbers
#define FLT_GUARD        0
#define FLT_MANT_DIG     24                      // # of bits in mantissa
#define FLT_MAX          3.402823466e+38F        // max value
#define FLT_MAX_10_EXP   38                      // max decimal exponent
#define FLT_MAX_EXP      128                     // max binary exponent
#define FLT_MIN          1.175494351e-38F        // min normalized positive value
#define FLT_MIN_10_EXP   (-37)                   // min decimal exponent
#define FLT_MIN_EXP      (-125)                  // min binary exponent
#define FLT_NORMALIZE    0
#define FLT_RADIX        2                       // exponent radix
#define FLT_TRUE_MIN     1.401298464e-45F        // min positive value

#define LDBL_DIG         DBL_DIG                 // # of decimal digits of precision
#define LDBL_EPSILON     DBL_EPSILON             // smallest such that 1.0+LDBL_EPSILON != 1.0
#define LDBL_HAS_SUBNORM DBL_HAS_SUBNORM         // type does support subnormal numbers
#define LDBL_MANT_DIG    DBL_MANT_DIG            // # of bits in mantissa
#define LDBL_MAX         DBL_MAX                 // max value
#define LDBL_MAX_10_EXP  DBL_MAX_10_EXP          // max decimal exponent
#define LDBL_MAX_EXP     DBL_MAX_EXP             // max binary exponent
#define LDBL_MIN         DBL_MIN                 // min normalized positive value
#define LDBL_MIN_10_EXP  DBL_MIN_10_EXP          // min decimal exponent
#define LDBL_MIN_EXP     DBL_MIN_EXP             // min binary exponent
#define _LDBL_RADIX      _DBL_RADIX              // exponent radix
#define LDBL_TRUE_MIN    DBL_TRUE_MIN            // min positive value

using namespace std;
using namespace sw::unum;

void TestConversionResult(bool bValid, string descriptor)
{
	if (!bValid) {
		std::cout << descriptor << " conversions FAIL" << std::endl;
	}
	else {
		std::cout << descriptor << " conversions PASS" << std::endl;
	}
}

template<size_t fbits>
bool ValidateValue() {
	const int NR_TEST_CASES = 12;
	float input[NR_TEST_CASES] = {
		0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024
	};
#if 0
	float golden_scales[NR_TEST_CASES] = {
		0, 0, 1, 2, 3,  4,  5,  6,   7,   8,   9,   10
	};
#endif
	float golden_answer[NR_TEST_CASES] = {
		0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024
	};

	bool bValid = true;
	for (int i = 0; i < NR_TEST_CASES; i++) {
		value<fbits> v;
		v = input[i];
		if (fabs(double(v) - golden_answer[i]) > 0.00000001) {
			cerr << "FAIL [" << setw(2) << i << "] input " << input[i] << " ref = " << golden_answer[i] << " != " << setw(5) << v << endl;
			bValid = false;
		}
	}
	for (int i = 2; i < NR_TEST_CASES; i++) {
		value<fbits> v;
		v = 1.0 / input[i];
		if (fabs(double(v) - (1.0 / golden_answer[i])) > 0.00000001) {
			cerr << "FAIL [" << setw(2) << NR_TEST_CASES + i << "] input " << 1.0 / input[i] << " ref = " << 1.0 / golden_answer[i] << " != " << setw(5) << v << endl;
			bValid = false;
		}
	}
	return bValid;
}

/*
minimum normalized positive value of float, double and long double respectively
FLT_MIN
DBL_MIN
LDBL_MIN

minimum positive value of float, double and long double respectively
(macro constant)
FLT_TRUE_MIN
DBL_TRUE_MIN    (C++17)
LDBL_TRUE_MIN
 */
template<size_t fbits>
bool ValidateSubnormalFloats() {
	constexpr float flt_min = std::numeric_limits<float>::min();
	constexpr float flt_max = std::numeric_limits<float>::max();

	bool bSuccess = false;

	std::cout << flt_min << " " << flt_max << std::endl;
	std::cout << FLT_TRUE_MIN << std::endl;
	std::cout << hexfloat << flt_min << defaultfloat << std::endl;

	value<23> v;
	float flt = flt_min;
	std::cout << components(v) << std::endl;
	for (size_t i = 0; i < 24; ++i) {
		flt /= 2.0;
		v = flt;
		std::cout << hexfloat << flt << defaultfloat << " " << flt << " " << components(v) << " " << v << std::endl;
	}

	flt = FLT_TRUE_MIN + flt_min;
	v = flt;
	std::cout << hexfloat << flt << defaultfloat << " " << flt << " " << components(v) << " " << v << std::endl;

	return bSuccess;
}

template<size_t fbits>
void PrintValue(float f, const value<fbits>& v) {
	cout << "float: " << setw(fbits) << f << components(v) << endl;
}

int main()
try {
	int nrOfFailedTestCases = 0;

	cout << "Value configuration validation" << endl;
	TestConversionResult(ValidateValue<8>(), "value<8>");

	cout << "Conversion values of importance" << endl;
	/*
	no exp left : geo-dw d          0.125  result          0.0625  scale = -4  k = -2  exp = -  0001 00010          0.0625     PASS
	no rounding alltaken u          0.125  result             0.5  scale = -1  k = -1  exp = 1  0011 00100            0.25 FAIL
	no rounding alltaken u           0.25  result               1  scale =  0  k = -1  exp = 0  0100 00100            0.25 FAIL
	no rounding alltaken d           0.25  result            0.25  scale = -2  k = -1  exp = 0  0010 00100            0.25     PASS
	no rounding alltaken u          -0.25  result           -0.25  scale=  -2  k=  -1  exp=   0  1110 11100           -0.25     PASS
	no rounding alltaken d          -0.25  result              -1  scale=   0  k=  -1  exp=   0  1100 11100           -0.25 FAIL
	no rounding alltaken d         -0.125  result            -0.5  scale=  -1  k=  -1  exp=   1  1101 11100           -0.25 FAIL
	no exp left:  geo-dw u         -0.125  result         -0.0625  scale=  -4  k=  -2  exp=   -  1111 11110         -0.0625     PASS
	*/
	float f;
	value<23> v;
	f =  0.12499f; v = f; PrintValue(f, v);
	f =  0.12500f; v = f; PrintValue(f, v);
	f =  0.12501f; v = f; PrintValue(f, v);
	f =  0.24999f; v = f; PrintValue(f, v);
	f =  0.25000f; v = f; PrintValue(f, v);
	f =  0.25001f; v = f; PrintValue(f, v);
	f = -0.25001f; v = f; PrintValue(f, v);
	f = -0.25000f; v = f; PrintValue(f, v);
	f = -0.24999f; v = f; PrintValue(f, v);
	f = -0.12501f; v = f; PrintValue(f, v);
	f = -0.12500f; v = f; PrintValue(f, v);
	f = -0.12499f; v = f; PrintValue(f, v);

	cout << "Rounding" << endl;
	bitblock<8> fraction;
	fraction = 0x55;
	value<8> r8(false, 0, fraction, false, false);
	cout << "Value is " << r8 << " components are " << components(r8) << endl;
	value<7> r7 = r8.round_to<7>();
	cout << "Value is " << r7 << " components are " << components(r7) << endl;
	value<6> r6 = r8.round_to<6>();
	cout << "Value is " << r6 << " components are " << components(r6) << endl;
	value<5> r5 = r8.round_to<5>();
	cout << "Value is " << r5 << " components are " << components(r5) << endl;
	value<4> r4 = r8.round_to<4>();
	cout << "Value is " << r4 << " components are " << components(r4) << endl;
	value<3> r3 = r8.round_to<3>();
	cout << "Value is " << r3 << " components are " << components(r3) << endl;
	value<2> r2 = r8.round_to<2>();
	cout << "Value is " << r2 << " components are " << components(r2) << endl;
	value<1> r1 = r8.round_to<1>();
	cout << "Value is " << r1 << " components are " << components(r1) << endl;
	value<0> r0 = r8.round_to<0>();
	cout << "Value is " << r0 << " components are " << components(r0) << endl;

	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	cerr << msg << endl;
	return EXIT_FAILURE;
}
catch (...) {
	cerr << "Caught unknown exception" << endl;
	return EXIT_FAILURE;
}
<commit_msg>fixing the work-around<commit_after>//  values.cpp : tests on values in scientific notation (sign, scale, fraction)
//
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.

#include "common.hpp"
//#define POSIT_VERBOSE_OUTPUT
#define POSIT_TRACE_CONVERSION
#include <posit>

/*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Constants
#define DBL_DECIMAL_DIG  17                      // # of decimal digits of rounding precision
#define DBL_DIG          15                      // # of decimal digits of precision
#define DBL_EPSILON      2.2204460492503131e-016 // smallest such that 1.0+DBL_EPSILON != 1.0
#define DBL_HAS_SUBNORM  1                       // type does support subnormal numbers
#define DBL_MANT_DIG     53                      // # of bits in mantissa
#define DBL_MAX          1.7976931348623158e+308 // max value
#define DBL_MAX_10_EXP   308                     // max decimal exponent
#define DBL_MAX_EXP      1024                    // max binary exponent
#define DBL_MIN          2.2250738585072014e-308 // min positive value
#define DBL_MIN_10_EXP   (-307)                  // min decimal exponent
#define DBL_MIN_EXP      (-1021)                 // min binary exponent
#define _DBL_RADIX       2                       // exponent radix
#define DBL_TRUE_MIN     4.9406564584124654e-324 // min positive value

#define FLT_DECIMAL_DIG  9                       // # of decimal digits of rounding precision
#define FLT_DIG          6                       // # of decimal digits of precision
#define FLT_EPSILON      1.192092896e-07F        // smallest such that 1.0+FLT_EPSILON != 1.0
#define FLT_HAS_SUBNORM  1                       // type does support subnormal numbers
#define FLT_GUARD        0
#define FLT_MANT_DIG     24                      // # of bits in mantissa
#define FLT_MAX          3.402823466e+38F        // max value
#define FLT_MAX_10_EXP   38                      // max decimal exponent
#define FLT_MAX_EXP      128                     // max binary exponent
#define FLT_MIN          1.175494351e-38F        // min normalized positive value
#define FLT_MIN_10_EXP   (-37)                   // min decimal exponent
#define FLT_MIN_EXP      (-125)                  // min binary exponent
#define FLT_NORMALIZE    0
#define FLT_RADIX        2                       // exponent radix
#define FLT_TRUE_MIN     1.401298464e-45F        // min positive value

#define LDBL_DIG         DBL_DIG                 // # of decimal digits of precision
#define LDBL_EPSILON     DBL_EPSILON             // smallest such that 1.0+LDBL_EPSILON != 1.0
#define LDBL_HAS_SUBNORM DBL_HAS_SUBNORM         // type does support subnormal numbers
#define LDBL_MANT_DIG    DBL_MANT_DIG            // # of bits in mantissa
#define LDBL_MAX         DBL_MAX                 // max value
#define LDBL_MAX_10_EXP  DBL_MAX_10_EXP          // max decimal exponent
#define LDBL_MAX_EXP     DBL_MAX_EXP             // max binary exponent
#define LDBL_MIN         DBL_MIN                 // min normalized positive value
#define LDBL_MIN_10_EXP  DBL_MIN_10_EXP          // min decimal exponent
#define LDBL_MIN_EXP     DBL_MIN_EXP             // min binary exponent
#define _LDBL_RADIX      _DBL_RADIX              // exponent radix
#define LDBL_TRUE_MIN    DBL_TRUE_MIN            // min positive value
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/

using namespace std;
using namespace sw::unum;

void TestConversionResult(bool bValid, string descriptor)
{
	if (!bValid) {
		std::cout << descriptor << " conversions FAIL" << std::endl;
	}
	else {
		std::cout << descriptor << " conversions PASS" << std::endl;
	}
}

template<size_t fbits>
bool ValidateValue() {
	const int NR_TEST_CASES = 12;
	float input[NR_TEST_CASES] = {
		0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024
	};
#if 0
	float golden_scales[NR_TEST_CASES] = {
		0, 0, 1, 2, 3,  4,  5,  6,   7,   8,   9,   10
	};
#endif
	float golden_answer[NR_TEST_CASES] = {
		0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024
	};

	bool bValid = true;
	for (int i = 0; i < NR_TEST_CASES; i++) {
		value<fbits> v;
		v = input[i];
		if (fabs(double(v) - golden_answer[i]) > 0.00000001) {
			cerr << "FAIL [" << setw(2) << i << "] input " << input[i] << " ref = " << golden_answer[i] << " != " << setw(5) << v << endl;
			bValid = false;
		}
	}
	for (int i = 2; i < NR_TEST_CASES; i++) {
		value<fbits> v;
		v = 1.0 / input[i];
		if (fabs(double(v) - (1.0 / golden_answer[i])) > 0.00000001) {
			cerr << "FAIL [" << setw(2) << NR_TEST_CASES + i << "] input " << 1.0 / input[i] << " ref = " << 1.0 / golden_answer[i] << " != " << setw(5) << v << endl;
			bValid = false;
		}
	}
	return bValid;
}

/*
minimum normalized positive value of float, double and long double respectively
FLT_MIN
DBL_MIN
LDBL_MIN

minimum positive value of float, double and long double respectively
(macro constant)
FLT_TRUE_MIN
DBL_TRUE_MIN    (C++17)
LDBL_TRUE_MIN
 */
template<size_t fbits>
bool ValidateSubnormalFloats() {
	constexpr float flt_min = std::numeric_limits<float>::min();
	constexpr float flt_max = std::numeric_limits<float>::max();
	constexpr float flt_true_min = 1.401298464e-45F;

	bool bSuccess = false;

	std::cout << flt_min << " " << flt_max << std::endl;
	std::cout << flt_true_min << std::endl;
	std::cout << hexfloat << flt_min << defaultfloat << std::endl;

	value<23> v;
	float flt = flt_min;
	std::cout << components(v) << std::endl;
	for (size_t i = 0; i < 24; ++i) {
		flt /= 2.0;
		v = flt;
		std::cout << hexfloat << flt << defaultfloat << " " << flt << " " << components(v) << " " << v << std::endl;
	}

	flt = flt_min + 3*flt_true_min;
	v = flt;
	std::cout << hexfloat << flt << defaultfloat << " " << flt << " " << components(v) << " " << v << std::endl;

	return bSuccess;
}

template<size_t fbits>
void PrintValue(float f, const value<fbits>& v) {
	cout << "float: " << setw(fbits) << f << components(v) << endl;
}

int main()
try {
	int nrOfFailedTestCases = 0;

	cout << "Validate subnormal floats" << endl;
	ValidateSubnormalFloats<std::numeric_limits<float>::digits>();

	cout << "Value configuration validation" << endl;
	TestConversionResult(ValidateValue<8>(), "value<8>");

	cout << "Conversion values of importance" << endl;
	/*
	no exp left : geo-dw d          0.125  result          0.0625  scale = -4  k = -2  exp = -  0001 00010          0.0625     PASS
	no rounding alltaken u          0.125  result             0.5  scale = -1  k = -1  exp = 1  0011 00100            0.25 FAIL
	no rounding alltaken u           0.25  result               1  scale =  0  k = -1  exp = 0  0100 00100            0.25 FAIL
	no rounding alltaken d           0.25  result            0.25  scale = -2  k = -1  exp = 0  0010 00100            0.25     PASS
	no rounding alltaken u          -0.25  result           -0.25  scale=  -2  k=  -1  exp=   0  1110 11100           -0.25     PASS
	no rounding alltaken d          -0.25  result              -1  scale=   0  k=  -1  exp=   0  1100 11100           -0.25 FAIL
	no rounding alltaken d         -0.125  result            -0.5  scale=  -1  k=  -1  exp=   1  1101 11100           -0.25 FAIL
	no exp left:  geo-dw u         -0.125  result         -0.0625  scale=  -4  k=  -2  exp=   -  1111 11110         -0.0625     PASS
	*/
	float f;
	value<23> v;
	f =  0.12499f; v = f; PrintValue(f, v);
	f =  0.12500f; v = f; PrintValue(f, v);
	f =  0.12501f; v = f; PrintValue(f, v);
	f =  0.24999f; v = f; PrintValue(f, v);
	f =  0.25000f; v = f; PrintValue(f, v);
	f =  0.25001f; v = f; PrintValue(f, v);
	f = -0.25001f; v = f; PrintValue(f, v);
	f = -0.25000f; v = f; PrintValue(f, v);
	f = -0.24999f; v = f; PrintValue(f, v);
	f = -0.12501f; v = f; PrintValue(f, v);
	f = -0.12500f; v = f; PrintValue(f, v);
	f = -0.12499f; v = f; PrintValue(f, v);

	cout << "Rounding" << endl;
	bitblock<8> fraction;
	fraction = 0x55;
	value<8> r8(false, 0, fraction, false, false);
	cout << "Value is " << r8 << " components are " << components(r8) << endl;
	value<7> r7 = r8.round_to<7>();
	cout << "Value is " << r7 << " components are " << components(r7) << endl;
	value<6> r6 = r8.round_to<6>();
	cout << "Value is " << r6 << " components are " << components(r6) << endl;
	value<5> r5 = r8.round_to<5>();
	cout << "Value is " << r5 << " components are " << components(r5) << endl;
	value<4> r4 = r8.round_to<4>();
	cout << "Value is " << r4 << " components are " << components(r4) << endl;
	value<3> r3 = r8.round_to<3>();
	cout << "Value is " << r3 << " components are " << components(r3) << endl;
	value<2> r2 = r8.round_to<2>();
	cout << "Value is " << r2 << " components are " << components(r2) << endl;
	value<1> r1 = r8.round_to<1>();
	cout << "Value is " << r1 << " components are " << components(r1) << endl;
	value<0> r0 = r8.round_to<0>();
	cout << "Value is " << r0 << " components are " << components(r0) << endl;

	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	cerr << msg << endl;
	return EXIT_FAILURE;
}
catch (...) {
	cerr << "Caught unknown exception" << endl;
	return EXIT_FAILURE;
}
<|endoftext|>


<commit_before><commit_msg>Lines authored by ritzau<commit_after><|endoftext|>


<commit_before>/*
Copyright 2021 Bruce Dawson All Rights Reserved.

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 <stdio.h>
#include <stdarg.h>
#include <Windows.h>

// Debug logging function inspired by this tweet:
// https://twitter.com/BruceDawson0xB/status/1263583135427465224

// NotepadLog() is a debugging logging function that finds an untitled notepad
// document and appends to it. All you have to do is open notepad and your debug
// logging statements will start showing up. It's like OutputDebugString, but
// without the need to attach a debugger.
// Fixes from the original include avoiding buffer overruns and handling the
// unsaved-changes marker (leading '*' character) in notepad.
// Don't leave NotepadLog statements in your shipping binaries or else unwitting
// users will find notepad filled with your debug statements.

static void NotepadLog(char * str, ...)
{
  va_list ap;
  va_start(ap, str);

  char buf[1000];
  vsnprintf_s(buf, _TRUNCATE, str, ap);
  va_end(ap);

  strncat_s(buf, _countof(buf), "\r\n", _TRUNCATE);
  HWND notepad = FindWindow(NULL, "Untitled - Notepad");
  if (!notepad)
    notepad = FindWindow(NULL, "*Untitled - Notepad");
  HWND edit = FindWindowEx(notepad, nullptr, "EDIT", nullptr);
  SendMessage(edit, EM_REPLACESEL, TRUE, (LPARAM)buf);
}

// Test code, prints whatever is in the first argument.
int main(int argc, char* argv[]) {
  NotepadLog("Message: '%s'", argv[1]);
}
<commit_msg>Fix NotepadLog for Unicode, C++ conformance<commit_after>/*
Copyright 2021 Bruce Dawson All Rights Reserved.

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 <stdio.h>
#include <stdarg.h>
#include <Windows.h>

// Debug logging function inspired by this tweet:
// https://twitter.com/BruceDawson0xB/status/1263583135427465224

// NotepadLog() is a debugging logging function that finds an untitled notepad
// document and appends to it. All you have to do is open notepad and your debug
// logging statements will start showing up. It's like OutputDebugString, but
// without the need to attach a debugger.
// Fixes from the original include avoiding buffer overruns and handling the
// unsaved-changes marker (leading '*' character) in notepad.
// Don't leave NotepadLog statements in your shipping binaries or else unwitting
// users will find notepad filled with your debug statements.

// Note that using the 'A' versions of FindWindow(Ex) is important to make this
// compile when built for unicode. Using SendMessageA is important to ensure
// that the message is interpreted correctly by notepad as ASCII.
static void NotepadLog(const char * str, ...)
{
  va_list ap;
  va_start(ap, str);

  char buf[1000];
  vsnprintf_s(buf, _TRUNCATE, str, ap);
  va_end(ap);

  strncat_s(buf, _countof(buf), "\r\n", _TRUNCATE);
  HWND notepad = FindWindowA(NULL, "Untitled - Notepad");
  if (!notepad)
    notepad = FindWindowA(NULL, "*Untitled - Notepad");
  HWND edit = FindWindowExA(notepad, nullptr, "EDIT", nullptr);
  SendMessageA(edit, EM_REPLACESEL, TRUE, (LPARAM)buf);
}

// Test code, prints whatever is in the first argument.
int main(int argc, char* argv[]) {
  NotepadLog("Message: '%s'", argv[1]);
}
<|endoftext|>


<commit_before>#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define ACTIVE_POWER                            0x050B
#define RMS_VOLTAGE                             0x0505
#define RMS_CURRENT                             0x0508
#define APPARENT_POWER                          0x050F

/*! Handle packets related to the ZCL electrical measurement cluster.
    \param ind the APS level data indication containing the ZCL packet
    \param zclFrame the actual ZCL frame which holds the electrical measurement cluster command or attribute
 */
void DeRestPluginPrivate::handleElectricalMeasurementClusterIndication(const deCONZ::ApsDataIndication &ind, const deCONZ::ZclFrame &zclFrame)
{
    if (zclFrame.isDefaultResponse())
    {
        return;
    }

    Sensor *sensor = getSensorNodeForAddressAndEndpoint(ind.srcAddress(), ind.srcEndpoint(), QLatin1String("ZHAPower"));

    if (!sensor)
    {
        DBG_Printf(DBG_INFO, "No power sensor found for 0x%016llX, endpoint: 0x%02X\n", ind.srcAddress().ext(), ind.srcEndpoint());
        return;
    }

    QDataStream stream(zclFrame.payload());
    stream.setByteOrder(QDataStream::LittleEndian);

    bool isReadAttr = false;
    bool isReporting = false;
    if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReadAttributesResponseId)
    {
        isReadAttr = true;
    }
    if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReportAttributesId)
    {
        isReporting = true;
    }

    // Read ZCL reporting and ZCL Read Attributes Response
    if (isReadAttr || isReporting)
    {
        const auto modelId = sensor->modelId();
        const NodeValue::UpdateType updateType = isReadAttr ? NodeValue::UpdateByZclRead : NodeValue::UpdateByZclReport;

        bool configUpdated = false;
        bool stateUpdated = false;

        while (!stream.atEnd())
        {
            quint16 attrId;
            quint8 attrTypeId;

            stream >> attrId;
            if (isReadAttr)
            {
                quint8 status;
                stream >> status;  // Read Attribute Response status
                if (status != deCONZ::ZclSuccessStatus)
                {
                    continue;
                }
            }
            stream >> attrTypeId;

            deCONZ::ZclAttribute attr(attrId, attrTypeId, QLatin1String(""), deCONZ::ZclRead, false);

            if (!attr.readFromStream(stream))
            {
                continue;
            }

            ResourceItem *item = nullptr;

            switch (attrId)
            {
            case ACTIVE_POWER:
            {
                qint16 power = attr.numericValue().s16;
                item = sensor->item(RStatePower);

                if (item && power != -32768)
                {
                    if (modelId == QLatin1String("SmartPlug") ||                              // Heiman
                        modelId.startsWith(QLatin1String("SKHMP30")) ||                       // GS smart plug
                        modelId.startsWith(QLatin1String("ROB_200")) ||                       // ROBB Smarrt micro dimmer
                        modelId.startsWith(QLatin1String("Micro Smart Dimmer")) ||            // Sunricher Micro Smart Dimmer
                        modelId.startsWith(QLatin1String("lumi.plug.maeu")) ||                // Xiaomi Aqara ZB3.0 smart plug
                        modelId == QLatin1String("RICI01") ||                                 // LifeControl Smart Plug
                        modelId.startsWith(QLatin1String("outlet")) ||                        // Samsung SmartThings IM6001-OTP/IM6001-OTP01
                        modelId == QLatin1String("3200-Sgb") ||                               // Samsung/Centralite smart outlet
                        modelId == QLatin1String("3200-de") ||                                // Samsung/Centralite smart outlet
                        modelId.startsWith(QLatin1String("lumi.switch.n0agl1")) ||            // Xiaomi Aqara Single Switch Module T1 (With Neutral)
                        modelId.startsWith(QLatin1String("lumi.switch.b1naus01")))            // Xiaomi ZB3.0 Smart Wall Switch
                    {
                        power = static_cast<qint16>(round((double)power / 10.0)); // 0.1W -> W
                    }
                    else if (modelId.startsWith(QLatin1String("Plug")) && sensor->manufacturer() == QLatin1String("OSRAM")) // OSRAM
                    {
                        power = power == 28000 ? 0 : power / 10;
                    }
                    else if (modelId.startsWith(QLatin1String("SZ-ESW01")))                   // Sercomm / Telstra smart plug
                    {
                        power = static_cast<qint16>(round(((double)power * 128) / 1000.0));
                    }
                    else if (modelId == QLatin1String("Connected socket outlet"))             // Niko smart socket
                    {
                        power = static_cast<qint16>(round(((double)power * 1123) / 10000.0));
                    }
                    else if (modelId.startsWith(QLatin1String("lumi.relay.c2acn")))           // Xiaomi relay
                    {
                        continue;   // Device seems to always report -1 via this cluster/attribute
                    }

                    if (item->toNumber() != power)
                    {
                        item->setValue(power); // in W
                        enqueueEvent(Event(RSensors, RStatePower, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            case RMS_VOLTAGE:
            {
                quint16 voltage = attr.numericValue().u16;
                item = sensor->item(RStateVoltage);

                if (item && voltage != 65535)
                {
                    if (modelId == QLatin1String("SmartPlug") ||                                       // Heiman
                        modelId.startsWith(QLatin1String("SPLZB-1")) ||                                // Develco smart plug
                        modelId.startsWith(QLatin1String("SMRZB-3")) ||                                // Develco smart relay
                        modelId.startsWith(QLatin1String("SMRZB-1")) ||                                // Develco smart cable
                        modelId.startsWith(QLatin1String("SKHMP30")) ||                                // GS smart plug
                        modelId == QLatin1String("Smart16ARelay51AU") ||                               // Aurora (Develco) smart plug
                        modelId == QLatin1String("PoP"))                                               // Apex Smart Plug
                    {
                        voltage = static_cast<quint16>(round((double)voltage / 100.0)); // 0.01V -> V
                    }
                    else if (modelId == QLatin1String("RICI01") ||                                     // LifeControl Smart Plug
                             modelId.startsWith(QLatin1String("outlet")) ||                            // Samsung SmartThings IM6001-OTP/IM6001-OTP01
                             modelId.startsWith(QLatin1String("EMIZB-1")) ||                           // Develco EMI
                             modelId.startsWith(QLatin1String("ROB_200")) ||                           // ROBB Smarrt micro dimmer
                             modelId.startsWith(QLatin1String("Micro Smart Dimmer")) ||                // Sunricher Micro Smart Dimmer
                             modelId == QLatin1String("Connected socket outlet") ||                    // Niko smart socket
                             modelId.startsWith(QLatin1String("TH112")))                               // Sinope Thermostats
                    {
                        voltage = static_cast<quint16>(round((double)voltage / 10.0)); // 0.1V -> V
                    }
                    else if (modelId.startsWith(QLatin1String("SZ-ESW01")))                            // Sercomm / Telstra smart plug
                    {
                        voltage = static_cast<quint16>(round((double)voltage / 125.0)); // -> V
                    }

                    if (item->toNumber() != voltage)
                    {
                        item->setValue(voltage); // in V
                        enqueueEvent(Event(RSensors, RStateVoltage, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            case RMS_CURRENT:
            {
                quint16 current = attr.numericValue().u16;
                item = sensor->item(RStateCurrent);

                if (item && current != 65535)
                {
                    if (modelId == QLatin1String("SP 120") ||                                     // innr
                        modelId.startsWith(QLatin1String("outlet")) ||                            // Samsung SmartThings IM6001-OTP/IM6001-OTP01
                        modelId == QLatin1String("DoubleSocket50AU") ||                           // Aurora
                        modelId.startsWith(QLatin1String("SPLZB-1")) ||                           // Develco smart plug
                        modelId == QLatin1String("Smart16ARelay51AU") ||                          // Aurora (Develco) smart plug
                        modelId == QLatin1String("RICI01") ||                                     // LifeControl Smart Plug
                        modelId.startsWith(QLatin1String("SZ-ESW01")) ||                          // Sercomm / Telstra smart plug
                        modelId == QLatin1String("TS0121") ||                                     // Tuya smart plug
                        modelId.startsWith(QLatin1String("ROB_200")) ||                           // ROBB Smarrt micro dimmer
                        modelId.startsWith(QLatin1String("Micro Smart Dimmer")) ||                // Sunricher Micro Smart Dimmer
                        modelId == QLatin1String("Connected socket outlet") ||                    // Niko smart socket
                        modelId == QLatin1String("SMRZB-1") ||                                    // Develco smart cable
                        modelId.startsWith(QLatin1String("S1")) ||                                // Ubisys S1/S1-R
                        modelId.startsWith(QLatin1String("S2")) ||                                // Ubisys S2/S2-R
                        modelId.startsWith(QLatin1String("J1")) ||                                // Ubisys J1/J1-R
                        modelId.startsWith(QLatin1String("D1")))                                  // Ubisys D1/D1-R
                    {
                        // already in mA
                    }
                    else if (modelId == QLatin1String("SmartPlug") ||                             // Heiman
                             modelId.startsWith(QLatin1String("EMIZB-1")) ||                      // Develco EMI
                             modelId.startsWith(QLatin1String("SKHMP30")) ||                      // GS smart plug
                             modelId == QLatin1String("3200-Sgb") ||                              // Samsung smart outlet
                             modelId == QLatin1String("3200-de") ||                               // Samsung smart outlet
                             modelId.startsWith(QLatin1String("SPW35Z")) ||                       // RT-RK OBLO SPW35ZD0 smart plug
                             modelId == QLatin1String("TH1300ZB"))                                // Sinope thermostat
                    {
                        current *= 10; // 0.01A -> mA
                    }
                    else
                    {
                        current *= 1000; // A -> mA
                    }

                    if (item->toNumber() != current)
                    {
                        item->setValue(current); // in mA
                        enqueueEvent(Event(RSensors, RStateCurrent, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            case APPARENT_POWER:
            {
                quint16 power = attr.numericValue().u16;
                item = sensor->item(RStatePower);

                if (item && power != 65535)
                {
                    if (modelId == QLatin1String("TH1300ZB")) // Sinope thermostat
                    {
                        power = static_cast<quint16>(round((double)power / 1000.0)); // -> W
                    }

                    if (item->toNumber() != power)
                    {
                        item->setValue(power); // in W
                        enqueueEvent(Event(RSensors, RStatePower, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            default:
                break;
            }
        }

        if (stateUpdated)
        {
            sensor->updateStateTimestamp();
            enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
        }

        if (configUpdated || stateUpdated)
        {
            updateSensorEtag(&*sensor);
            sensor->setNeedSaveDatabase(true);
            queSaveDb(DB_SENSORS, DB_SHORT_SAVE_DELAY);
        }
    }
}
<commit_msg>Fix Develco EMIZB-132 voltage calculation<commit_after>#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define ACTIVE_POWER                            0x050B
#define RMS_VOLTAGE                             0x0505
#define RMS_CURRENT                             0x0508
#define APPARENT_POWER                          0x050F

/*! Handle packets related to the ZCL electrical measurement cluster.
    \param ind the APS level data indication containing the ZCL packet
    \param zclFrame the actual ZCL frame which holds the electrical measurement cluster command or attribute
 */
void DeRestPluginPrivate::handleElectricalMeasurementClusterIndication(const deCONZ::ApsDataIndication &ind, const deCONZ::ZclFrame &zclFrame)
{
    if (zclFrame.isDefaultResponse())
    {
        return;
    }

    Sensor *sensor = getSensorNodeForAddressAndEndpoint(ind.srcAddress(), ind.srcEndpoint(), QLatin1String("ZHAPower"));

    if (!sensor)
    {
        DBG_Printf(DBG_INFO, "No power sensor found for 0x%016llX, endpoint: 0x%02X\n", ind.srcAddress().ext(), ind.srcEndpoint());
        return;
    }

    QDataStream stream(zclFrame.payload());
    stream.setByteOrder(QDataStream::LittleEndian);

    bool isReadAttr = false;
    bool isReporting = false;
    if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReadAttributesResponseId)
    {
        isReadAttr = true;
    }
    if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReportAttributesId)
    {
        isReporting = true;
    }

    // Read ZCL reporting and ZCL Read Attributes Response
    if (isReadAttr || isReporting)
    {
        const auto modelId = sensor->modelId();
        const NodeValue::UpdateType updateType = isReadAttr ? NodeValue::UpdateByZclRead : NodeValue::UpdateByZclReport;

        bool configUpdated = false;
        bool stateUpdated = false;

        while (!stream.atEnd())
        {
            quint16 attrId;
            quint8 attrTypeId;

            stream >> attrId;
            if (isReadAttr)
            {
                quint8 status;
                stream >> status;  // Read Attribute Response status
                if (status != deCONZ::ZclSuccessStatus)
                {
                    continue;
                }
            }
            stream >> attrTypeId;

            deCONZ::ZclAttribute attr(attrId, attrTypeId, QLatin1String(""), deCONZ::ZclRead, false);

            if (!attr.readFromStream(stream))
            {
                continue;
            }

            ResourceItem *item = nullptr;

            switch (attrId)
            {
            case ACTIVE_POWER:
            {
                qint16 power = attr.numericValue().s16;
                item = sensor->item(RStatePower);

                if (item && power != -32768)
                {
                    if (modelId == QLatin1String("SmartPlug") ||                              // Heiman
                        modelId.startsWith(QLatin1String("SKHMP30")) ||                       // GS smart plug
                        modelId.startsWith(QLatin1String("ROB_200")) ||                       // ROBB Smarrt micro dimmer
                        modelId.startsWith(QLatin1String("Micro Smart Dimmer")) ||            // Sunricher Micro Smart Dimmer
                        modelId.startsWith(QLatin1String("lumi.plug.maeu")) ||                // Xiaomi Aqara ZB3.0 smart plug
                        modelId == QLatin1String("RICI01") ||                                 // LifeControl Smart Plug
                        modelId.startsWith(QLatin1String("outlet")) ||                        // Samsung SmartThings IM6001-OTP/IM6001-OTP01
                        modelId == QLatin1String("3200-Sgb") ||                               // Samsung/Centralite smart outlet
                        modelId == QLatin1String("3200-de") ||                                // Samsung/Centralite smart outlet
                        modelId.startsWith(QLatin1String("lumi.switch.n0agl1")) ||            // Xiaomi Aqara Single Switch Module T1 (With Neutral)
                        modelId.startsWith(QLatin1String("lumi.switch.b1naus01")))            // Xiaomi ZB3.0 Smart Wall Switch
                    {
                        power = static_cast<qint16>(round((double)power / 10.0)); // 0.1W -> W
                    }
                    else if (modelId.startsWith(QLatin1String("Plug")) && sensor->manufacturer() == QLatin1String("OSRAM")) // OSRAM
                    {
                        power = power == 28000 ? 0 : power / 10;
                    }
                    else if (modelId.startsWith(QLatin1String("SZ-ESW01")))                   // Sercomm / Telstra smart plug
                    {
                        power = static_cast<qint16>(round(((double)power * 128) / 1000.0));
                    }
                    else if (modelId == QLatin1String("Connected socket outlet"))             // Niko smart socket
                    {
                        power = static_cast<qint16>(round(((double)power * 1123) / 10000.0));
                    }
                    else if (modelId.startsWith(QLatin1String("lumi.relay.c2acn")))           // Xiaomi relay
                    {
                        continue;   // Device seems to always report -1 via this cluster/attribute
                    }

                    if (item->toNumber() != power)
                    {
                        item->setValue(power); // in W
                        enqueueEvent(Event(RSensors, RStatePower, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            case RMS_VOLTAGE:
            {
                quint16 voltage = attr.numericValue().u16;
                item = sensor->item(RStateVoltage);

                if (item && voltage != 65535)
                {
                    if (modelId == QLatin1String("SmartPlug") ||                                       // Heiman
                        modelId.startsWith(QLatin1String("SPLZB-1")) ||                                // Develco smart plug
                        modelId.startsWith(QLatin1String("SMRZB-3")) ||                                // Develco smart relay
                        modelId.startsWith(QLatin1String("SMRZB-1")) ||                                // Develco smart cable
                        modelId.startsWith(QLatin1String("SKHMP30")) ||                                // GS smart plug
                        modelId == QLatin1String("Smart16ARelay51AU") ||                               // Aurora (Develco) smart plug
                        modelId == QLatin1String("PoP"))                                               // Apex Smart Plug
                    {
                        voltage = static_cast<quint16>(round((double)voltage / 100.0)); // 0.01V -> V
                    }
                    else if (modelId == QLatin1String("RICI01") ||                                     // LifeControl Smart Plug
                             modelId.startsWith(QLatin1String("outlet")) ||                            // Samsung SmartThings IM6001-OTP/IM6001-OTP01
                             modelId.startsWith(QLatin1String("ROB_200")) ||                           // ROBB Smarrt micro dimmer
                             modelId.startsWith(QLatin1String("Micro Smart Dimmer")) ||                // Sunricher Micro Smart Dimmer
                             modelId == QLatin1String("Connected socket outlet") ||                    // Niko smart socket
                             modelId.startsWith(QLatin1String("TH112")))                               // Sinope Thermostats
                    {
                        voltage = static_cast<quint16>(round((double)voltage / 10.0)); // 0.1V -> V
                    }
                    else if (modelId.startsWith(QLatin1String("SZ-ESW01")))                            // Sercomm / Telstra smart plug
                    {
                        voltage = static_cast<quint16>(round((double)voltage / 125.0)); // -> V
                    }

                    if (item->toNumber() != voltage)
                    {
                        item->setValue(voltage); // in V
                        enqueueEvent(Event(RSensors, RStateVoltage, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            case RMS_CURRENT:
            {
                quint16 current = attr.numericValue().u16;
                item = sensor->item(RStateCurrent);

                if (item && current != 65535)
                {
                    if (modelId == QLatin1String("SP 120") ||                                     // innr
                        modelId.startsWith(QLatin1String("outlet")) ||                            // Samsung SmartThings IM6001-OTP/IM6001-OTP01
                        modelId == QLatin1String("DoubleSocket50AU") ||                           // Aurora
                        modelId.startsWith(QLatin1String("SPLZB-1")) ||                           // Develco smart plug
                        modelId == QLatin1String("Smart16ARelay51AU") ||                          // Aurora (Develco) smart plug
                        modelId == QLatin1String("RICI01") ||                                     // LifeControl Smart Plug
                        modelId.startsWith(QLatin1String("SZ-ESW01")) ||                          // Sercomm / Telstra smart plug
                        modelId == QLatin1String("TS0121") ||                                     // Tuya smart plug
                        modelId.startsWith(QLatin1String("ROB_200")) ||                           // ROBB Smarrt micro dimmer
                        modelId.startsWith(QLatin1String("Micro Smart Dimmer")) ||                // Sunricher Micro Smart Dimmer
                        modelId == QLatin1String("Connected socket outlet") ||                    // Niko smart socket
                        modelId == QLatin1String("SMRZB-1") ||                                    // Develco smart cable
                        modelId.startsWith(QLatin1String("S1")) ||                                // Ubisys S1/S1-R
                        modelId.startsWith(QLatin1String("S2")) ||                                // Ubisys S2/S2-R
                        modelId.startsWith(QLatin1String("J1")) ||                                // Ubisys J1/J1-R
                        modelId.startsWith(QLatin1String("D1")))                                  // Ubisys D1/D1-R
                    {
                        // already in mA
                    }
                    else if (modelId == QLatin1String("SmartPlug") ||                             // Heiman
                             modelId.startsWith(QLatin1String("EMIZB-1")) ||                      // Develco EMI
                             modelId.startsWith(QLatin1String("SKHMP30")) ||                      // GS smart plug
                             modelId == QLatin1String("3200-Sgb") ||                              // Samsung smart outlet
                             modelId == QLatin1String("3200-de") ||                               // Samsung smart outlet
                             modelId.startsWith(QLatin1String("SPW35Z")) ||                       // RT-RK OBLO SPW35ZD0 smart plug
                             modelId == QLatin1String("TH1300ZB"))                                // Sinope thermostat
                    {
                        current *= 10; // 0.01A -> mA
                    }
                    else
                    {
                        current *= 1000; // A -> mA
                    }

                    if (item->toNumber() != current)
                    {
                        item->setValue(current); // in mA
                        enqueueEvent(Event(RSensors, RStateCurrent, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            case APPARENT_POWER:
            {
                quint16 power = attr.numericValue().u16;
                item = sensor->item(RStatePower);

                if (item && power != 65535)
                {
                    if (modelId == QLatin1String("TH1300ZB")) // Sinope thermostat
                    {
                        power = static_cast<quint16>(round((double)power / 1000.0)); // -> W
                    }

                    if (item->toNumber() != power)
                    {
                        item->setValue(power); // in W
                        enqueueEvent(Event(RSensors, RStatePower, sensor->id(), item));
                    }
                }

                sensor->setZclValue(updateType, ind.srcEndpoint(), ELECTRICAL_MEASUREMENT_CLUSTER_ID, attrId, attr.numericValue());
                stateUpdated = true;
            }
                break;

            default:
                break;
            }
        }

        if (stateUpdated)
        {
            sensor->updateStateTimestamp();
            enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
        }

        if (configUpdated || stateUpdated)
        {
            updateSensorEtag(&*sensor);
            sensor->setNeedSaveDatabase(true);
            queSaveDb(DB_SENSORS, DB_SHORT_SAVE_DELAY);
        }
    }
}
<|endoftext|>


<commit_before>#include <set>
#include <vector>
#include <string>
#include <cassert>

#ifndef HAVE_LLVM
#error "This code needs LLVM enabled"
#endif

#include <llvm/Config/llvm-config.h>

#if (LLVM_VERSION_MAJOR < 3)
#error "Unsupported version of LLVM"
#endif

// ignore unused parameters in LLVM libraries
#if (__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

#include <llvm/IR/Module.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/IR/DebugInfoMetadata.h>

#if (__clang__)
#pragma clang diagnostic pop // ignore -Wunused-parameter
#else
#pragma GCC diagnostic pop
#endif

#include "dg/tools/llvm-slicer-utils.h"
#include "dg/llvm/LLVMNode.h"
#include "dg/llvm/LLVMDependenceGraph.h"
#include "dg/ADT/Queue.h"
#include "dg/llvm/PointerAnalysis/PointerAnalysis.h"

using namespace dg;

using llvm::errs;

// mapping of AllocaInst to the names of C variables
static std::map<const llvm::Value *, std::string> valuesToVariables;

static bool usesTheVariable(LLVMDependenceGraph& dg,
                            const llvm::Value *v,
                            const std::string& var)
{
    auto pts = dg.getPTA()->getLLVMPointsTo(v);
    if (pts.empty() || pts.hasUnknown())
        return true; // it may be a definition of the variable, we do not know

    for (const auto& ptr : pts) {
        auto name = valuesToVariables.find(ptr.value);
        if (name != valuesToVariables.end()) {
            if (name->second == var)
                return true;
        }
    }

    return false;
}

template <typename InstT>
static bool useOfTheVar(LLVMDependenceGraph& dg,
                        const llvm::Instruction& I,
                        const std::string& var)
{
    // check that we store to that variable
    const InstT *tmp = llvm::dyn_cast<InstT>(&I);
    if (!tmp)
        return false;

    return usesTheVariable(dg, tmp->getPointerOperand(), var);
}

static bool isStoreToTheVar(LLVMDependenceGraph& dg,
                            const llvm::Instruction& I,
                            const std::string& var)
{
    return useOfTheVar<llvm::StoreInst>(dg, I, var);
}

static bool isLoadOfTheVar(LLVMDependenceGraph& dg,
                           const llvm::Instruction& I,
                           const std::string& var)
{
    return useOfTheVar<llvm::LoadInst>(dg, I, var);
}

static bool instMatchesCrit(LLVMDependenceGraph& dg,
                            const llvm::Instruction& I,
                            const std::vector<std::pair<int, std::string>>& parsedCrit)
{
    for (const auto& c : parsedCrit) {
        auto& Loc = I.getDebugLoc();
#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7)
        if (Loc.getLine() <= 0) {
#else
        if (!Loc) {
#endif
            continue;
        }

        if (static_cast<int>(Loc.getLine()) != c.first)
            continue;

        if (isStoreToTheVar(dg, I, c.second) ||
            isLoadOfTheVar(dg, I, c.second)) {
            llvm::errs() << "Matched line " << c.first << " with variable "
                         << c.second << " to:\n" << I << "\n";
            return true;
        }
    }

    return false;
}

static bool globalMatchesCrit(const llvm::GlobalVariable& G,
                              const std::vector<std::pair<int, std::string>>& parsedCrit)
{
    for (const auto& c : parsedCrit) {
        if (c.first != -1)
            continue;
        if (c.second == G.getName().str()) {
            llvm::errs() << "Matched global variable "
                         << c.second << " to:\n" << G << "\n";
            return true;
        }
    }

    return false;
}

static inline bool isNumber(const std::string& s) {
    assert(!s.empty());

    for (const auto c : s)
        if (!isdigit(c))
            return false;

    return true;
}

static void getLineCriteriaNodes(LLVMDependenceGraph& dg,
                                 std::vector<std::string>& criteria,
                                 std::set<LLVMNode *>& nodes)
{
    assert(!criteria.empty() && "No criteria given");

    std::vector<std::pair<int, std::string>> parsedCrit;
    for (auto& crit : criteria) {
        auto parts = splitList(crit, ':');
        assert(parts.size() == 2);

        // parse the line number
        if (parts[0].empty()) {
            // global variable
            parsedCrit.emplace_back(-1, parts[1]);
        } else if (isNumber(parts[0])) {
            int line = atoi(parts[0].c_str());
            if (line > 0)
                parsedCrit.emplace_back(line, parts[1]);
        } else {
            llvm::errs() << "Invalid line: '" << parts[0] << "'. "
                         << "Needs to be a number or empty for global variables.\n";
        }
    }

    assert(!parsedCrit.empty() && "Failed parsing criteria");

#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7)
    llvm::errs() << "WARNING: Variables names matching is not supported for LLVM older than 3.7\n";
    llvm::errs() << "WARNING: The slicing criteria with variables names will not work\n";
#else
    // create the mapping from LLVM values to C variable names
    for (auto& it : getConstructedFunctions()) {
        for (auto& I : llvm::instructions(*llvm::cast<llvm::Function>(it.first))) {
            if (const llvm::DbgDeclareInst *DD = llvm::dyn_cast<llvm::DbgDeclareInst>(&I)) {
                auto val = DD->getAddress();
                valuesToVariables[val] = DD->getVariable()->getName().str();
            } else if (const llvm::DbgValueInst *DV
                        = llvm::dyn_cast<llvm::DbgValueInst>(&I)) {
                auto val = DV->getValue();
                valuesToVariables[val] = DV->getVariable()->getName().str();
            }
        }
    }

    bool no_dbg = valuesToVariables.empty();
    if (no_dbg) {
        llvm::errs() << "No debugging information found in program,\n"
                     << "slicing criteria with lines and variables will work\n"
                     << "only for global variables.\n"
                     << "You can still use the criteria based on call sites ;)\n";
    }

    for (const auto& GV : dg.getModule()->globals()) {
        valuesToVariables[&GV] = GV.getName().str();
    }

    // try match globals
    for (auto& G : dg.getModule()->globals()) {
        if (globalMatchesCrit(G, parsedCrit)) {
            LLVMNode *nd = dg.getGlobalNode(&G);
            assert(nd);
            nodes.insert(nd);
        }
    }

    // we do not have any mapping, we will not match anything
    if (no_dbg) {
        return;
    }

    // map line criteria to nodes
    for (auto& it : getConstructedFunctions()) {
        for (auto& I : llvm::instructions(*llvm::cast<llvm::Function>(it.first))) {
            if (instMatchesCrit(dg, I, parsedCrit)) {
                LLVMNode *nd = it.second->getNode(&I);
                assert(nd);
                nodes.insert(nd);
            }
        }
    }
#endif // LLVM > 3.6
}

static std::set<LLVMNode *> _mapToNextInstr(LLVMDependenceGraph&,
                                            const std::set<LLVMNode *>& callsites) {
    std::set<LLVMNode *> nodes;

    for (LLVMNode *cs: callsites) {
        llvm::Instruction *I = llvm::dyn_cast<llvm::Instruction>(cs->getValue());
        assert(I && "Callsite is not an instruction");
        llvm::Instruction *succ = I->getNextNode();
        if (!succ) {
            llvm::errs() << *I << "has no successor that could be criterion\n";
            // abort for now
            abort();
        }

        LLVMDependenceGraph *local_dg = cs->getDG();
        LLVMNode *node = local_dg->getNode(succ);
        assert(node && "DG does not have such node");
        nodes.insert(node);
    }

    return nodes;
}

static
std::set<LLVMNode *> getPrimarySlicingCriteriaNodes(LLVMDependenceGraph& dg,
                                                    const std::string& slicingCriteria,
                                                    bool criteria_are_next_instr) {
    std::set<LLVMNode *> nodes;
    std::vector<std::string> criteria = splitList(slicingCriteria);
    assert(!criteria.empty() && "Did not get slicing criteria");

    std::vector<std::string> line_criteria;
    std::vector<std::string> node_criteria;
    std::tie(line_criteria, node_criteria)
        = splitStringVector(criteria, [](std::string& s) -> bool
            { return s.find(':') != std::string::npos; }
          );

    // if user wants to slice with respect to the return of main,
    // insert the ret instructions to the nodes.
    for (const auto& c : node_criteria) {
        if (c == "ret") {
            LLVMNode *exit = dg.getExit();
            // We could insert just the exit node, but this way we will
            // get annotations to the functions.
            for (auto it = exit->rev_control_begin(), et = exit->rev_control_end();
                 it != et; ++it) {
                nodes.insert(*it);
            }
        }
    }

    // map the criteria to nodes
    if (!node_criteria.empty())
        dg.getCallSites(node_criteria, &nodes);
    if (!line_criteria.empty())
        getLineCriteriaNodes(dg, line_criteria, nodes);

    if (criteria_are_next_instr && !nodes.empty()) {
        // the given criteria are just markers for the
        // next instruction, so map the criteria to
        // the next instructions
        auto mappedNodes = _mapToNextInstr(dg, nodes);
        nodes.swap(mappedNodes);
    }

    return nodes;
}

static std::pair<std::set<std::string>, std::set<std::string>>
parseSecondarySlicingCriteria(const std::string& slicingCriteria)
{
    std::vector<std::string> criteria = splitList(slicingCriteria);

    std::set<std::string> control_criteria;
    std::set<std::string> data_criteria;

    // if user wants to slice with respect to the return of main,
    // insert the ret instructions to the nodes.
    for (const auto& c : criteria) {
        auto s = c.size();
        if (s > 2 && c[s - 2] == '(' && c[s - 1] == ')')
            data_criteria.insert(c.substr(0, s - 2));
        else
            control_criteria.insert(c);
    }

    return {control_criteria, data_criteria};
}

// FIXME: copied from LLVMDependenceGraph.cpp, do not duplicate the code
static bool isCallTo(LLVMNode *callNode, const std::set<std::string>& names)
{
    using namespace llvm;

    if (!isa<llvm::CallInst>(callNode->getValue())) {
        return false;
    }

    // if the function is undefined, it has no subgraphs,
    // but is not called via function pointer
    if (!callNode->hasSubgraphs()) {
        const CallInst *callInst = cast<CallInst>(callNode->getValue());
#if LLVM_VERSION_MAJOR >= 8
        const Value *calledValue = callInst->getCalledOperand();
#else
        const Value *calledValue = callInst->getCalledValue();
#endif
        const Function *func = dyn_cast<Function>(calledValue->stripPointerCasts());
        // in the case we haven't run points-to analysis
        if (!func)
            return false;

        return array_match(func->getName(), names);
    } else {
        // simply iterate over the subgraphs, get the entry node
        // and check it
        for (LLVMDependenceGraph *dg : callNode->getSubgraphs()) {
            LLVMNode *entry = dg->getEntry();
            assert(entry && "No entry node in graph");

            const Function *func
                = cast<Function>(entry->getValue()->stripPointerCasts());
            return array_match(func->getName(), names);
        }
    }

    return false;
}

static inline
void checkSecondarySlicingCrit(std::set<LLVMNode *>& criteria_nodes,
                               const std::set<std::string>& secondaryControlCriteria,
                               const std::set<std::string>& secondaryDataCriteria,
                               LLVMNode *nd) {
    if (isCallTo(nd, secondaryControlCriteria))
        criteria_nodes.insert(nd);
    if (isCallTo(nd, secondaryDataCriteria)) {
        llvm::errs() << "WARNING: Found possible data secondary slicing criterion: "
                    << *nd->getValue() << "\n";
        llvm::errs() << "This is not fully supported, so adding to be sound\n";
        criteria_nodes.insert(nd);
    }
}

// mark nodes that are going to be in the slice
static
bool findSecondarySlicingCriteria(std::set<LLVMNode *>& criteria_nodes,
                                  const std::set<std::string>& secondaryControlCriteria,
                                  const std::set<std::string>& secondaryDataCriteria)
{
    // FIXME: do this more efficiently (and use the new DFS class)
    std::set<LLVMBBlock *> visited;
    ADT::QueueLIFO<LLVMBBlock *> queue;
    auto tmp = criteria_nodes;
    for (auto&c : tmp) {
        // the criteria may be a global variable and in that
        // case it has no basic block (but also no predecessors,
        // so we can skip it)
        if (!c->getBBlock())
            continue;

        queue.push(c->getBBlock());
        visited.insert(c->getBBlock());

        for (auto nd : c->getBBlock()->getNodes()) {
            if (nd == c)
                break;

            if (nd->hasSubgraphs()) {
                // we search interprocedurally
                for (auto dg : nd->getSubgraphs()) {
                    auto exit = dg->getExitBB();
                    assert(exit && "No exit BB in a graph");
                    if (visited.insert(exit).second)
                        queue.push(exit);
                }
            }

            checkSecondarySlicingCrit(criteria_nodes,
                                      secondaryControlCriteria,
                                      secondaryDataCriteria, nd);
        }
    }

    // get basic blocks
    while (!queue.empty()) {
        auto cur = queue.pop();
        for (auto pred : cur->predecessors()) {
            for (auto nd : pred->getNodes()) {
                if (nd->hasSubgraphs()) {
                    // we search interprocedurally
                    for (auto dg : nd->getSubgraphs()) {
                        auto exit = dg->getExitBB();
                        assert(exit && "No exit BB in a graph");
                        if (visited.insert(exit).second)
                            queue.push(exit);
                    }
                }

                checkSecondarySlicingCrit(criteria_nodes,
                                          secondaryControlCriteria,
                                          secondaryDataCriteria, nd);
            }
            if (visited.insert(pred).second)
                queue.push(pred);
        }
    }

    return true;
}

bool getSlicingCriteriaNodes(LLVMDependenceGraph& dg,
                             const std::string& slicingCriteria,
                             const std::string& secondarySlicingCriteria,
                             std::set<LLVMNode *>& criteria_nodes,
                             bool criteria_are_next_instr) {

    auto nodes = getPrimarySlicingCriteriaNodes(dg,
                                                slicingCriteria,
                                                criteria_are_next_instr);
    if (nodes.empty()) {
        return true; // no criteria found
    }

    criteria_nodes.swap(nodes);

    auto secondaryCriteria
        = parseSecondarySlicingCriteria(secondarySlicingCriteria);
    const auto& secondaryControlCriteria = secondaryCriteria.first;
    const auto& secondaryDataCriteria = secondaryCriteria.second;

    // mark nodes that are going to be in the slice
    if (!findSecondarySlicingCriteria(criteria_nodes,
                                      secondaryControlCriteria,
                                      secondaryDataCriteria)) {
        llvm::errs() << "Finding secondary slicing criteria nodes failed\n";
        return false;
    }

    return true;
}

<commit_msg>slicer crits: use getAccessedMemory instead of directly PTA when matching crits<commit_after>#include <set>
#include <vector>
#include <string>
#include <cassert>

#ifndef HAVE_LLVM
#error "This code needs LLVM enabled"
#endif

#include <llvm/Config/llvm-config.h>

#if (LLVM_VERSION_MAJOR < 3)
#error "Unsupported version of LLVM"
#endif

// ignore unused parameters in LLVM libraries
#if (__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

#include <llvm/IR/Module.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/IR/DebugInfoMetadata.h>

#if (__clang__)
#pragma clang diagnostic pop // ignore -Wunused-parameter
#else
#pragma GCC diagnostic pop
#endif

#include "dg/tools/llvm-slicer-utils.h"
#include "dg/llvm/LLVMNode.h"
#include "dg/llvm/LLVMDependenceGraph.h"
#include "dg/ADT/Queue.h"
#include "dg/llvm/PointerAnalysis/PointerAnalysis.h"

using namespace dg;

using llvm::errs;

// mapping of AllocaInst to the names of C variables
static std::map<const llvm::Value *, std::string> valuesToVariables;

static bool usesTheVariable(LLVMDependenceGraph& dg,
                            const llvm::Instruction& I,
                            const std::string& var)
{
    if (!I.mayReadOrWriteMemory())
        return false;

    auto memacc  =dg.getPTA()->getAccessedMemory(&I);
    if (memacc.first) {
        // PTA has no information, it may be a definition of the variable,
        // we do not know
        return true;
    }

    for (const auto& region : memacc.second) {
        auto name = valuesToVariables.find(region.pointer.value);
        if (name != valuesToVariables.end()) {
            if (name->second == var)
                return true;
        }
    }

    return false;
}

static bool instMatchesCrit(LLVMDependenceGraph& dg,
                            const llvm::Instruction& I,
                            const std::vector<std::pair<int, std::string>>& parsedCrit)
{
    for (const auto& c : parsedCrit) {
        auto& Loc = I.getDebugLoc();
#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7)
        if (Loc.getLine() <= 0) {
#else
        if (!Loc) {
#endif
            continue;
        }

        if (static_cast<int>(Loc.getLine()) != c.first)
            continue;

        if (usesTheVariable(dg, I, c.second)) {
            llvm::errs() << "Matched line " << c.first << " with variable "
                         << c.second << " to:\n" << I << "\n";
            return true;
        }
    }

    return false;
}

static bool globalMatchesCrit(const llvm::GlobalVariable& G,
                              const std::vector<std::pair<int, std::string>>& parsedCrit)
{
    for (const auto& c : parsedCrit) {
        if (c.first != -1)
            continue;
        if (c.second == G.getName().str()) {
            llvm::errs() << "Matched global variable "
                         << c.second << " to:\n" << G << "\n";
            return true;
        }
    }

    return false;
}

static inline bool isNumber(const std::string& s) {
    assert(!s.empty());

    for (const auto c : s)
        if (!isdigit(c))
            return false;

    return true;
}

static void getLineCriteriaNodes(LLVMDependenceGraph& dg,
                                 std::vector<std::string>& criteria,
                                 std::set<LLVMNode *>& nodes)
{
    assert(!criteria.empty() && "No criteria given");

    std::vector<std::pair<int, std::string>> parsedCrit;
    for (auto& crit : criteria) {
        auto parts = splitList(crit, ':');
        assert(parts.size() == 2);

        // parse the line number
        if (parts[0].empty()) {
            // global variable
            parsedCrit.emplace_back(-1, parts[1]);
        } else if (isNumber(parts[0])) {
            int line = atoi(parts[0].c_str());
            if (line > 0)
                parsedCrit.emplace_back(line, parts[1]);
        } else {
            llvm::errs() << "Invalid line: '" << parts[0] << "'. "
                         << "Needs to be a number or empty for global variables.\n";
        }
    }

    assert(!parsedCrit.empty() && "Failed parsing criteria");

#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7)
    llvm::errs() << "WARNING: Variables names matching is not supported for LLVM older than 3.7\n";
    llvm::errs() << "WARNING: The slicing criteria with variables names will not work\n";
#else
    // create the mapping from LLVM values to C variable names
    for (auto& it : getConstructedFunctions()) {
        for (auto& I : llvm::instructions(*llvm::cast<llvm::Function>(it.first))) {
            if (const llvm::DbgDeclareInst *DD = llvm::dyn_cast<llvm::DbgDeclareInst>(&I)) {
                auto val = DD->getAddress();
                valuesToVariables[val] = DD->getVariable()->getName().str();
            } else if (const llvm::DbgValueInst *DV
                        = llvm::dyn_cast<llvm::DbgValueInst>(&I)) {
                auto val = DV->getValue();
                valuesToVariables[val] = DV->getVariable()->getName().str();
            }
        }
    }

    bool no_dbg = valuesToVariables.empty();
    if (no_dbg) {
        llvm::errs() << "No debugging information found in program,\n"
                     << "slicing criteria with lines and variables will work\n"
                     << "only for global variables.\n"
                     << "You can still use the criteria based on call sites ;)\n";
    }

    for (const auto& GV : dg.getModule()->globals()) {
        valuesToVariables[&GV] = GV.getName().str();
    }

    // try match globals
    for (auto& G : dg.getModule()->globals()) {
        if (globalMatchesCrit(G, parsedCrit)) {
            LLVMNode *nd = dg.getGlobalNode(&G);
            assert(nd);
            nodes.insert(nd);
        }
    }

    // we do not have any mapping, we will not match anything
    if (no_dbg) {
        return;
    }

    // map line criteria to nodes
    for (auto& it : getConstructedFunctions()) {
        for (auto& I : llvm::instructions(*llvm::cast<llvm::Function>(it.first))) {
            if (instMatchesCrit(dg, I, parsedCrit)) {
                LLVMNode *nd = it.second->getNode(&I);
                assert(nd);
                nodes.insert(nd);
            }
        }
    }
#endif // LLVM > 3.6
}

static std::set<LLVMNode *> _mapToNextInstr(LLVMDependenceGraph&,
                                            const std::set<LLVMNode *>& callsites) {
    std::set<LLVMNode *> nodes;

    for (LLVMNode *cs: callsites) {
        llvm::Instruction *I = llvm::dyn_cast<llvm::Instruction>(cs->getValue());
        assert(I && "Callsite is not an instruction");
        llvm::Instruction *succ = I->getNextNode();
        if (!succ) {
            llvm::errs() << *I << "has no successor that could be criterion\n";
            // abort for now
            abort();
        }

        LLVMDependenceGraph *local_dg = cs->getDG();
        LLVMNode *node = local_dg->getNode(succ);
        assert(node && "DG does not have such node");
        nodes.insert(node);
    }

    return nodes;
}

static
std::set<LLVMNode *> getPrimarySlicingCriteriaNodes(LLVMDependenceGraph& dg,
                                                    const std::string& slicingCriteria,
                                                    bool criteria_are_next_instr) {
    std::set<LLVMNode *> nodes;
    std::vector<std::string> criteria = splitList(slicingCriteria);
    assert(!criteria.empty() && "Did not get slicing criteria");

    std::vector<std::string> line_criteria;
    std::vector<std::string> node_criteria;
    std::tie(line_criteria, node_criteria)
        = splitStringVector(criteria, [](std::string& s) -> bool
            { return s.find(':') != std::string::npos; }
          );

    // if user wants to slice with respect to the return of main,
    // insert the ret instructions to the nodes.
    for (const auto& c : node_criteria) {
        if (c == "ret") {
            LLVMNode *exit = dg.getExit();
            // We could insert just the exit node, but this way we will
            // get annotations to the functions.
            for (auto it = exit->rev_control_begin(), et = exit->rev_control_end();
                 it != et; ++it) {
                nodes.insert(*it);
            }
        }
    }

    // map the criteria to nodes
    if (!node_criteria.empty())
        dg.getCallSites(node_criteria, &nodes);
    if (!line_criteria.empty())
        getLineCriteriaNodes(dg, line_criteria, nodes);

    if (criteria_are_next_instr && !nodes.empty()) {
        // the given criteria are just markers for the
        // next instruction, so map the criteria to
        // the next instructions
        auto mappedNodes = _mapToNextInstr(dg, nodes);
        nodes.swap(mappedNodes);
    }

    return nodes;
}

static std::pair<std::set<std::string>, std::set<std::string>>
parseSecondarySlicingCriteria(const std::string& slicingCriteria)
{
    std::vector<std::string> criteria = splitList(slicingCriteria);

    std::set<std::string> control_criteria;
    std::set<std::string> data_criteria;

    // if user wants to slice with respect to the return of main,
    // insert the ret instructions to the nodes.
    for (const auto& c : criteria) {
        auto s = c.size();
        if (s > 2 && c[s - 2] == '(' && c[s - 1] == ')')
            data_criteria.insert(c.substr(0, s - 2));
        else
            control_criteria.insert(c);
    }

    return {control_criteria, data_criteria};
}

// FIXME: copied from LLVMDependenceGraph.cpp, do not duplicate the code
static bool isCallTo(LLVMNode *callNode, const std::set<std::string>& names)
{
    using namespace llvm;

    if (!isa<llvm::CallInst>(callNode->getValue())) {
        return false;
    }

    // if the function is undefined, it has no subgraphs,
    // but is not called via function pointer
    if (!callNode->hasSubgraphs()) {
        const CallInst *callInst = cast<CallInst>(callNode->getValue());
#if LLVM_VERSION_MAJOR >= 8
        const Value *calledValue = callInst->getCalledOperand();
#else
        const Value *calledValue = callInst->getCalledValue();
#endif
        const Function *func = dyn_cast<Function>(calledValue->stripPointerCasts());
        // in the case we haven't run points-to analysis
        if (!func)
            return false;

        return array_match(func->getName(), names);
    } else {
        // simply iterate over the subgraphs, get the entry node
        // and check it
        for (LLVMDependenceGraph *dg : callNode->getSubgraphs()) {
            LLVMNode *entry = dg->getEntry();
            assert(entry && "No entry node in graph");

            const Function *func
                = cast<Function>(entry->getValue()->stripPointerCasts());
            return array_match(func->getName(), names);
        }
    }

    return false;
}

static inline
void checkSecondarySlicingCrit(std::set<LLVMNode *>& criteria_nodes,
                               const std::set<std::string>& secondaryControlCriteria,
                               const std::set<std::string>& secondaryDataCriteria,
                               LLVMNode *nd) {
    if (isCallTo(nd, secondaryControlCriteria))
        criteria_nodes.insert(nd);
    if (isCallTo(nd, secondaryDataCriteria)) {
        llvm::errs() << "WARNING: Found possible data secondary slicing criterion: "
                    << *nd->getValue() << "\n";
        llvm::errs() << "This is not fully supported, so adding to be sound\n";
        criteria_nodes.insert(nd);
    }
}

// mark nodes that are going to be in the slice
static
bool findSecondarySlicingCriteria(std::set<LLVMNode *>& criteria_nodes,
                                  const std::set<std::string>& secondaryControlCriteria,
                                  const std::set<std::string>& secondaryDataCriteria)
{
    // FIXME: do this more efficiently (and use the new DFS class)
    std::set<LLVMBBlock *> visited;
    ADT::QueueLIFO<LLVMBBlock *> queue;
    auto tmp = criteria_nodes;
    for (auto&c : tmp) {
        // the criteria may be a global variable and in that
        // case it has no basic block (but also no predecessors,
        // so we can skip it)
        if (!c->getBBlock())
            continue;

        queue.push(c->getBBlock());
        visited.insert(c->getBBlock());

        for (auto nd : c->getBBlock()->getNodes()) {
            if (nd == c)
                break;

            if (nd->hasSubgraphs()) {
                // we search interprocedurally
                for (auto dg : nd->getSubgraphs()) {
                    auto exit = dg->getExitBB();
                    assert(exit && "No exit BB in a graph");
                    if (visited.insert(exit).second)
                        queue.push(exit);
                }
            }

            checkSecondarySlicingCrit(criteria_nodes,
                                      secondaryControlCriteria,
                                      secondaryDataCriteria, nd);
        }
    }

    // get basic blocks
    while (!queue.empty()) {
        auto cur = queue.pop();
        for (auto pred : cur->predecessors()) {
            for (auto nd : pred->getNodes()) {
                if (nd->hasSubgraphs()) {
                    // we search interprocedurally
                    for (auto dg : nd->getSubgraphs()) {
                        auto exit = dg->getExitBB();
                        assert(exit && "No exit BB in a graph");
                        if (visited.insert(exit).second)
                            queue.push(exit);
                    }
                }

                checkSecondarySlicingCrit(criteria_nodes,
                                          secondaryControlCriteria,
                                          secondaryDataCriteria, nd);
            }
            if (visited.insert(pred).second)
                queue.push(pred);
        }
    }

    return true;
}

bool getSlicingCriteriaNodes(LLVMDependenceGraph& dg,
                             const std::string& slicingCriteria,
                             const std::string& secondarySlicingCriteria,
                             std::set<LLVMNode *>& criteria_nodes,
                             bool criteria_are_next_instr) {

    auto nodes = getPrimarySlicingCriteriaNodes(dg,
                                                slicingCriteria,
                                                criteria_are_next_instr);
    if (nodes.empty()) {
        return true; // no criteria found
    }

    criteria_nodes.swap(nodes);

    auto secondaryCriteria
        = parseSecondarySlicingCriteria(secondarySlicingCriteria);
    const auto& secondaryControlCriteria = secondaryCriteria.first;
    const auto& secondaryDataCriteria = secondaryCriteria.second;

    // mark nodes that are going to be in the slice
    if (!findSecondarySlicingCriteria(criteria_nodes,
                                      secondaryControlCriteria,
                                      secondaryDataCriteria)) {
        llvm::errs() << "Finding secondary slicing criteria nodes failed\n";
        return false;
    }

    return true;
}

<|endoftext|>